Haskell Notları – 2 (Tipler)

Bir önceki yazının sonunda bir listenin neye benzediğini görmüştük. Listelere geçmeden önce Haskell’deki tip(type) sisteminden bahsedelim, sonra da listelere giriş yapalım.

Tipler

Diğer dillerden alışık olduğumuz tipleri Haskell’de de görüyoruz. Örnek vermek gerekirse, "deneme" bir String, True bir Bool tipine sahip. GHCI üzerinde, bir şeyin tipini merak ettiğinizde yapmanız gereken :t komutunu kullanmak, şöyle ki:

Prelude> :t True
True :: Bool
Prelude> :t "deneme"
"deneme" :: [Char]
Prelude> :t (3 < 2)
(3 < 2) :: Bool

Yukarıda "deneme"nin bir String olduğunu söylemiştim fakat GHCI bize "deneme"nin bir [Char] olduğunu söylüyor.

Prelude> : 'A'
'A' :: Char

Char tipi tek bir harfi ifade eder. [Char] ise görüntüsünden de anlaşılacağı üzere Char‘lardan oluşan bir listeyi ifade eder. Dolayısıyla String dediğimiz şey aslında Char‘lardan oluşan bir listeye verilen takma addır.

Yukarıda da gördüğünüz üzere :: sembolü tip notasyonunu ifade etmek için kullanılıyor.

Prelude> "deneme" :: [Char]
"deneme"

Burada yaptığımız şey "deneme"nin türünü kendi elimizle belirtmek oldu. Normalde GHCI "deneme"nin ne olduğunu kendisi anlayabiliyor. Fakat anlaşılmaz durumları çözmek, fonksiyonları daha okunabilir kılmak için bu notasyonu da kullanacağız. Az önce String‘in [Char] için bir takma ad olduğunu söylemiştim, dolayısıyla "deneme" :: String yazmak da "deneme"yi tanımlamak için geçerli bir yöntem ve daha okunaklı.

Prelude> "deneme" :: String
"deneme"

Şu ana kadar Bool, Char ve String tiplerini gördük. Haskell’in sayılar için biraz daha karışık bir tip sistemi var ve ondan daha sonra bahsedeceğiz. Şimdilik sayılar için Int, Double, Float gibi tipler olduğunu bilmemiz yeterli.

Fonksiyon tiplerini anlamak için not fonksiyonuna bir göz atalım.

Prelude> not True
False
Prelude> not (False && True)
True

Fonksiyonun ne yaptığı gayet açık. Peki fonksiyonun tipine bakmak istersek:

not :: Bool -> Bool

Karşımıza çıkan şey, not fonksiyonunun bir Bool parametresi alıp, sonuç olarak bir Bool değeri döndürdüğünü gösteriyor. Şimdi kendimiz bir fonksiyon tanımlayıp, tipinin ne olduğuna bakalım:

Prelude> let concatenate s1 s2 = s1 ++ s2
Prelude> :t concatenate 
concatenate :: [a] -> [a] -> [a]

Burada yeni bir çok şey var, sırayla neler olduğunu açıklayayım. Öncelikle let anahtar kelimesini GHCI üzerinde bir şeyler tanımlamak için kullanıyoruz. Aslında daha farklı kullanımları da var fakat şimdilik bu kullanımı bilmek yetecektir. İsterseniz concatenate fonksiyonumuzu let kelimesini kullanmadan bir dosyaya kaydedip bu dosyayı GHCI’ye yükleyebiliriz, bir önceki yazıda yaptığımız gibi. (:l dosyaAdı.hs)

++ fonksiyonu ise bir listeyi diğerinin arkasına eklemek için kullanılan bir fonksiyon. Listeler kısmında daha ayrıntılı göreceğiz.

a ise bir tip değişkeni. a’yı “herhangi bir tip” diye okuyabiliriz. O zaman [a]‘yı ise “herhangi bir şeyin listesi” diye okuyabiliriz. Dolayısıyla bizim fonksiyonumuz iki listeyi alıp, birleştiren bir fonksiyon ve listelerin içinde bulundurduğu tipler önemli değil, sadece fonksiyona gönderilen listelerinin ikisinin de aynı tipleri içeriyor olması gerekmekte.

Prelude> concatenate [1,2,3] [4,5,6,7]
[1,2,3,4,5,6,7]
Prelude> concatenate "has" "kell"
"haskell"
Prelude> concatenate [True] [False, False]
[True,False,False]
Prelude> concatenate [True] "haskell"

<interactive>:54:20: error:
    • Couldn't match type ‘Char’ with ‘Bool’
    ...

Zaten fonksiyon tanımına baktığımız zaman, GHCI bize parametrelerin [a] ve [a] olduğunu söylüyor. Yani a bir Char ise parametrelerimiz [Char] ve [Char] olacaktır, [Char] ve [Bool] olması mümkün değil. Bir de fark ettiyseniz, parametreler ve döndürülen değer için ayrı bir gösterim yok. Yani [a] -> [a] -> [a] ifadesinde baştaki iki [a] parametreleri belirtirken, sondaki [a] ise döndürülen değeri ifade ediyor.

Yazdığımız fonksiyon belirttiğim gibi herhangi bir tipten iki listeyi birleştiriyor. Peki biz sadece iki String’i birleştiren bir fonksiyon yazmak isteseydik? O zaman bir Haskell dosyası açalım ve içine bunları yazıp dosyayı GHCI’ye yükleyelim:

concatenate :: String -> String -> String
concatenate s1 s2 = s1 ++ s2

Gördüğünüz üzere fonksiyon tanımını biz yapmış olduk. Dolayısıyla bu fonksiyon artık sadece String’leri birleştirecek, başka tipten bir parametre verilmesi halinde hata verecektir. Fonksiyonları yazarken tanımlarını yapmak iyi bir alışkanlık, tam olarak ne yazacağınızı da bilmiyorsanız :t komutundan yardım almaktan da çekinmeyin.

Leave a Reply