Rehber Haskell Programlama Dili 1. Bölüm

DERS:1 HASKELL'E GİRİŞ

Ders 1'i okuduktan sonra:
* Haskell gelişimi için araçlar kurabileceksiniz,
* GHC ve GHCi'yi kullanabileceksiniz,
* Haskell programlarını yazma ipuçlarını kullanabileceksiniz.

Haskell'e Hoş Geldiniz
Haskell'e dalmadan önce, yolculuğunuzda kullanıyor olacağınız temel araçlara aşina olmanız gerekir. Bu ders Haskell'e başlangıç konusunu sizinle şöyle bir inceler. Ders, Haskell programlarını yazmak, derlemek ve çalıştırmak için gerekli temel şeyleri indirmeyle başlar. Daha sonra örnek kodlara bakacak ve Haskell'de nasıl kod yazacağınızı düşünmeye başlayacaksınız. Bu dersten sonra ayrıntılara dalmaya hazır olacaksınız.

1. Haskell Platformu

Yeni bir programlama dili öğrenmenin en kötü yanı, ilk sefer için geliştirme ortamınızı kurmayı başarmaktır. Allahtan ve biraz şaşırtıcı bir şekilde, bu durum artık Haskell ile ilgili tüm şeylerde bir sorun değildir. Haskell topluluğu, Haskell Platformu olarak anılan yararlı araçların tek yani toplu ve basit bir yüklenebilir paketini geliştirdiler. Yani Haskell Platformu, bir programlama dilini paketlemenin "piller dahildir" modelidir.

Haskell Platformu aşağıdakileri kapsar:
* Glasgow Haskell derleyicisi (GHC),
* Etkileşimli bir yorumlayıcı (GHCi),
* Haskell projelerini yönetmeyi sağlayan Stack aracı,
* Bir çift yararlı Haskell paketi.

Haskell Platformu, www.haskell.org/downloads#platform adresinden indirilebilir. Buradan, seçtiğiniz işletim sistemindeki yükleme talimatlarını takip edin. Bu kitap Haskell 8.0.1 veya daha üst sürümlerini kullanır.

2. Metin Editörleri

Şimdi Haskell Platformu'nu kurduğunuza göre, muhtemelen hangi editörü kullanmanız gerektiğini merak ediyorsunuz. Haskell, sizi hacklemeden önce güçlü bir şekilde "düşünmeye" teşvik eden bir dildir. Sonuç olarak, Haskell programları son derece kısa ve öz olma eğilimindedir. Girinti yönetme ve yararlı sözdizimi vurgulama özelliğinden başka, bunu sizin için yapabilecek küçük bir editör var. Birçok Haskell geliştiricisi "haskell-mode" özelliği olan Emacs'i kullanıyor. Fakat halihazırda Emacs'e aşina değilseniz (veya onunla çalışmaktan hoşlanmıyorsanız), kesinlikle Haskell'e ek olarak bir de Emacs'i ögrenmeye çalışmaya değmez. Önerim şudur ki; en çok kullandığınız herhangi bir editör için, bir Haskell eklentisi aramanızdır. Pico ve Notepad++ gibi sade bir metin editörü, bu kitap için gayet iyi çalışacaktır ve birçok tam teşekküllü IDE'lerin Haskell eklentileri vardır.

Glasgow Haskell Derleyicisi (GHC)

Haskell derlenmiş bir dildir ve Glasgow Haskell derleyicisi, Haskell'in o kadar güçlü olmasının sebebidir. Derleyicinin işi, insanlar tarafından okunabilen kaynak kodunu, makine tarafından okunabilir ikili(binary) haline dönüştürmektir. Derleme sonunda, yürütülebilir bir ikili(binary) dosyanız olur. Bu örneğin, başka bir programın, kaynak kodunuzda okuduğu ve onu anında yorumladığı(ki bu bir interpreter/yorumlayıcı ile gerçekleştirilir) Ruby gibi bir dili çalıştırma yönteminden farklıdır. Bir yorumlayıcıdan(interpreter) fazla olarak, bir derleyicinin(compiler) ana faydası şudur; derleyici kodu daha önceden dönüştürdüğü için, yazdığınız kodun analizi ve optimizasyonunu gerçekleştirebilir. Haskell'in diğer birkaç tasarım özelliklerinden yani onun güçlü bir tür(type) sisteminden dolayı, bir atasözü ya da vecize vardır; "eğer derlerse çalışır". GHC'yi sık sık kullanıyor olacağınıza rağmen, asla bunu kabullenmeye çalışmayın. GHC kendi çapında inanılmaz bir yazılım parçasıdır. GHC'yi başlatmak için, aşağıdaki gibi, terminali açin ve içine " ghc" yazın:
$ ghc
Bu metinde ne zaman bir "$" işaretiyle karşılaşırsanız; bu, bir komut istemcisine yazıyor olduğunuz anlamına gelir. Tabiki derlenecek dosya yoksa, GHC bu durumdan şikayetçi olacaktır. Başlamak için "hello.hs" adlı basit bir dosya oluşturacaksınız. Seçtiğiniz metin editöründe, "hello.hs" adlı yeni bir dosya oluşturun ve aşağıdaki kodu girin:

--hello.hs ilk Haskell dosyam
main = do
print"Merhaba dünya!.."

Buna göre:
* --hello.hs ilk Haskell dosyam ===> Dosya adınızla yorumlu bir satır,
* main = do ===> "main" fonksiyonunuzun başlangıç kısmı
* print "Merhaba dünya!.." ===> "main"
fonksiyonu "Merhaba dünya!.." metnini yazdırır.

Bu noktada, bu bölümdeki herhangi bir kodda neler olduğu hakkında çok fazla endişelenmeyin. Buradaki gerçek amacınız, ihtiyacınız olan araçları öğrenmektir yani Haskell'i öğrenmenize engel değildirler.
Artık örnek bir dosyanız(hello.hs) olduğuna göre, bu sefer bir argüman olarak "hello.hs" adlı dosyanızı geçerek tekrar GHC'yi çalıştırabilirsiniz:

$ ghc hello.hs
[1 of 1] Compiling Main
Linking hello ...

Eğer derleme başarılı olduysa, GHC üç adet dosya oluşturmuş olacaktır:
1- hello (windows'ta hello.exe)
2- hello.hi
3- hello.o
Öncelikle, en önemli dosya ikili(binary) yürütülebilir dosyanız olan "hello" adlı dosyasıdır. Çünkü bu dosya ikili(binary) bir yürütülebilir dosyadır, bu dosyayı basitçe şu şekilde çalıştırabilirsiniz:

$ ./hello
"Merhaba dünya!.."

Derlenmiş programın(hello) varsayılan davranışının, "main" fonksiyonu içindeki faaliyetleri yürütmek olduğuna dikkat edin. Varsayılan olarak, derliyor olduğunuz tüm Haskell programları, bir "main" fonksiyonuna ihtiyaç duyar ki bu "main" fonksiyonu Java, C#, C++ veya Python'daki " __main__" bloğu gibi, "main metoduna" benzer bir rol oynar.
Birçok komut satırı araçları gibi GHC de, çok çeşitli isteğe bağlı bayraklar ya da işaretler destekler. Örneğin, "hello.hs" dosyasını "helloworld" adlı bir yürütülebilir dosya adıyla derlemek isterseniz, "-o" işaretini aşağıdaki gibi kullanabilirsiniz:

$ghc hello.hs -o helloworld
[1 of 1] Compiling Main
Linking helloworld ....

Derleme seçeneklerinin daha tam bir listesi için, komut satırında "ghc --help (dosya adı argümanı gerektirmez)" çağrısını kullanın.

İpucu: "hello.hs" dosyası içindeki kodu kopyalayıp "testprogram" adıyla kendi yürütülebilir dosyanızı derleyebilirsiniz. Kodu basitçe bir dosyaya kopyalayın ve sonra bunu aynı klasörün içinde dosya olarak çalıştırın, yani:

$ ghc hello.hs -o testprogram

Haskell İle Etkileşime Geçme (GHCi)

Haskell programlarını yazmayı sağlayan en yararlı araçlardan biri, GHC için etkileşimli bir yorumlayıcı olan GHCi'dir. Tıpkı GHC'de olduğu gibi, GHCi de basit bir komutla başlatılır: yani "ghci" komutu. GHCi'yi başlattığınızda, aşağıdaki gibi yeni bir komut istemiyle karşılanacaksınız:

$ ghci
GHCi>

Bu kitap, GHCi'yi kullanırken; girdi aldığınız satırlar için "GHCİ>" satırını ve GHCi tarafından sağlanan çıktı satırları için ise bir boşluk(whitespace) belirtir. Komut satırından başlattığınız herhangi bir program hakkında öğrenilecek ilk şey, o başlattığınız komut satırından nasıl çıkacağını bilmektir. GHCi'de, GHCi komut istemcisinden çıkış yapmak için aşağıdaki gibi " :q " komutunu kullanın:

$ ghci
GHCi> :q
Leaving GHCi.

GHCi ile çalışmak, Python ve Ruby gibi birçok yorumlanan programlama dilindeki yorumlayıcılarla çalışmaya çok benzerdir. Bu yorumlayıcı basit bir hesap makinesi olarak kullanılabilir:

GHCi> 1 + 1
2

GHCi yorumlayıcısında çalısırken direkt kod da yazabilirsiniz:

GHCi> x = 2 + 2
GHCi> x
4
GHCi'nin sekizinci sürümünden önce, fonksiyon ve değişken tanımlamalarının aşağıdaki gibi bir "let" anahtar kelimesiyle başlatılması gerekiyordu. Buna artık gerek yok ama web'de ve daha eski kitaplardaki birçok Haskell örneklerinde hala "let" ifadesi kullanılmaktadır:

GHCi> let f x = x + x
GHCi> f 2
4

GHCi'nin en önemli kullanımı, yazıyor olduğunuz programlarla etkileşim kurmaktır. GHCi'nin içerisine mevcut bir dosyayı yüklemenin iki yolu vardır. İlk yolu, aşağıdaki gibi, dosya adını GHCi'ye bir argüman olarak geçmektir:

$ ghci hello.hs
[1 of 1] Compiling Main
Ok, modules loaded: Main.

Diğer yolu ise, aşağıdaki gibi etkileşimli oturumda " :l (veya :load) " komutunu kullanmaktır,

$ ghci
GHCi> :l hello.hs
[1 of 1] Compiling Main
Ok, modules loaded: Main.

Her iki durumda da, yazdığınız fonksiyonları daha sonradan çağırabilirsiniz:

GHCi> :l hello.hs
GHCi> main
"Merhaba dünya!.."

GHC'de dosyaları derlemekten farklı olarak, dosyalarınızın GHCi' nin içerisine yüklenebilmesi için bir "main" fonksiyonuna ihtiyaç duyulmaz. Bir dosya yüklediğinizde, fonksiyon ve değişkenlerin mevcut tanımlamalarını üzerine yazaktırsınız. O dosya üzerinde çalışırken, sürekli dosyalarınızı yükleyebilir ve değişiklikler yapabilirsiniz.
Haskell, doğal ve kullanımı kolay bir etkileşimli ortam olmakla birlikte, güçlü derleyici desteğine sahip olma konusunda da oldukça eşsizdir. Eğer Python, Ruby veya Javascripte gibi yorumlanmış bir dilden geliyorsanız, GHCi'ı kullanarak kendinizi evinizde gibi hissedeceksiniz. Eğer Java, C# veya C++ gibi derlenmiş dillere aşinaysanız, benzer şekilde Haskell kodlarınızı yazarken de derlenmiş bir dil ile çalışıyor olduğunuza şaşıracaksınız.
İpucu: Merhaba dünya adlı script'ınızı, isminizle "Merhaba <isim>" şeklinde söylemek için, düzenleyin. Bunu GHCi'ye tekrar yükleyin ve test edin. Doğal olarak bir isminiz olduğu için dosyanızı aşağıdaki gibi düzenleyin:

main = do
print "Merhaba Ömer!"

GHCi yorumlayıcınsı içerisindeyken, dosyanızı aşağıdaki gibi yükleyin:

GHCi> :l hello.hs
GHCi> main
Merhaba Ömer!

Haskell Kodunu Yazmak Ve Onunla Çalışmak

Haskell'e yeni gelenler için en sinir bozucu sorunlardan biri, Haskell'deki temel G(girdi)/Ç(çıktı) - (I/O)'nin oldukça gelişmiş bir konu olmasıdır. Genellikle bir dilde yeniyken, bir programın nasıl çalıştığını anladığınızdan emin olmak için yol boyunca çıktıları yazdırmak yaygın bir kalıptır. Haskell'de bu tür plansız hata ayıklama, genellikle imkansızdır. Oldukça karmaşık bir hatayla birlikte, bir Haskell programında bug bulmak basittir ve nasıl ilerleyeceğinize dair mutlak bir kaybınzın olması da kolaydır.
Haskell'in harika derleyicisinin kodunuzun doğruluğu konusunda da katı olması bu sorunu daha da karmaşıklaştırıyor. Bir program yazmaya, onu çalıştırmaya ve yaptığınız hataları hızlıca düzeltmeye alışkınsanız eğer, Haskell sizi hayal kırıklığına uğratır. Haskell, programları çalıştırmadan önce, zaman ayırmayı ve problemler üzerinde düşünmeyi güçlü bir şekilde ödüllendirir. Haskell'de tecrübe kazandıktan sonra, eminim ki bu hayal kırıklıkları, dilin en sevdiğiniz özelliklerinden bazıları olacak. Derleme sırasında doğruluğa takıntılı olmanın diğer tarafı; programlar çalışacak ve büyük ihtimalle de alıştığınızdan çok daha sık beklendiği gibi çalışacaktır. Minimal hüsranla Haskell kodu yazmanın püf noktası; kodu küçük parçalar halinde yazmak ve etkileşimli olarak her parçayla yazıldığı gibi oynamaktır. Bunu göstermek için, dağınık bir Haskell programı görecek ve onu düzelteceksiniz, böylece her bir parçasını anlamak kolaylaşacaktır. Bu örnekte, yazarlardan okuyuculara teşekkür mailleri tasarlayacak olan bir komut satırı uygulaması yazacağız. "first_prog.hs' adlı programın kabaca yazılan ilk sürümü aşağıdadır:

messyMain :: IO()
messyMain = do
print "E-posta kimin için?"
recipient <- getLine
print "Başlık ne?"
title <- getLine
print "Yazar kim?"
author <- getLine
print ("Sayın " ++ recipient ++ ",\n" ++
"Satın alım için teşekkürler " ++ title ++
"\nteşekkürler,\n" ++ author )

Esas mesele şudur; bu kod "messyMain" adlı büyük monolitik bir fonksiyonun içerisindedir. Modüler kod yazmanın iyi bir pratik olduğu tavsiyesi, yazılımda oldukça evrenseldir fakat bu durum Haskell'de yazılan ve anlayıp sorun giderebileceğiniz kod için bir esastır. Yukarıdaki program dağınık ya da karmaşık olmasına rağmen çalısır. Eğer "messMain" in adını "main" e değiştirseydiniz, bu programı derleyebilir ve çalıştırabilirdiniz. Ama bu kodu "first_prog.hs" dosyanızla aynı klasörde olduğunuzu varsayarak, olduğu gibi GHCi'ye de yükleyebilirdiniz:

$ghci
GHCi> :l first_prog.hs
[1 of 1] Compiling Main ( first_prog.hs, interpreted )
Ok, modules loaded: Main.

Eğer GHCi'den "Ok" sinyalini alıyorsanız, kodunuzun derlendiğini ve oldukça iyi çalıştığını biliyorsunuzdur. GHCi'nin, bir main fonksiyonunuzun olup olmadığını önemsemediğine, dikkat edin. Bu durum, bir "main" fonksiyonu olmayan dosyalarla hala etkileşime geçebileceğiniz için harikadır. Şimdi bir test sürüşü için kodunuzu inceleyebilirsiniz:

GHCi> messyMain
"Who is the email for?"
Happy Reader
"What is the Title?"
Learn Haskell
"Who is the Author?"
Will Kurt
"Dear Happy Reader,\nThanks for buying Learn Haskell\nthanks,\nWill Kurt"

Her şey iyi çalışıyor ancak kod biraz dağınıksa, onunla çalışmak biraz daha basit olabilirdi. İlk önceliğiniz bir e-posta oluşturmaktır fakat görmesi kolaydır ki o e-posta bir araya getirilen üç parçadan oluşmaktadır:
1- (e-postayı) alıcı bölümü
2 - kalıp
3 - (kişiye özel) imza

Bu bölümleri, onların kendi fonksiyonlarının içerisine çıkararak başlatacaksınız. Aşağıdaki kod "first_prog.hs" dosyanıza yazılır. Bu kitapta tanımlanan fonksiyon ve değerlerin neredeyse tamamı, şu anda çalışıyor olduğunuz dosyanın içerisine yazılacağı varsayılabilir. Aşağidaki gibi sadece "toPart" fonksiyonuyla başlayacaksınız:

toPart recipient = "Dear" ++ recipient ++ ",\n"

Bu örnekte, bu üç fonksiyonu birlikte yazabilirdiniz fakat genellikle siz ilerledikçe her bir fonksiyonu tek tek test etmek ve yavaş bir şekilde çalışmak çok daha iyidir. Bunu denemek için, dosyanızı aşağıdaki gibi tekrar GHCi'ye yükleyeceksiniz:

GHCi> :l "first_prog.hs"
[1 of 1] Compiling Main ( first_prog.hs, interpreted )
Ok, modules loaded: Main.
GHCi> toPart "Happy Reader"
"DearHappy Reader,\n"
GHCi> toPart "Bob Smith"
"DearBob Smith,\n"

Bu kodu bir editörde yazma ve daha sonra onu GHCi'ye yükleme ve tekrar yükleme şekli, kitap boyunca kod ile çalışmak anlamına gelen birincil önceliğiniz olacaktır. Tekrarlardan kaçınmak için, " :l first_prog.hs " işlemi, burada açıkça yazılmak yerine sadece yazıldığı farzedilmiştir.

Şimdi bunu GHCi'ye yüklediğinize göre, küçük bir hatanın, yani Dear ve alıcının ismi arasında gözden kaçan bir alanın (daha doğrusu birbirine bitişik olma hatasının) var olduğunu göreceksiniz. Hadi şimdi bunun nasıl düzeltileceğini görelim(toPart fonksiyonunun doğru hali):

toPart recipient = "Dear " ++ recipient ++ ",\n"

Ve GHCi'ye geri dönün

GHCi> toPart "Jane Doe"
"Dear Jane Doe,\n"

Herşey yolunda görünüyor. Şimdi sıra, diğer iki fonksiyonunuzu tanımlamada. Bu sefer ikisini de aynı anda yazacağız. Takip ederken, tek seferde bir fonksiyon kodu yazmak, onu GHCI'ye yüklemek ve devam etmeden önce hepsinin çalıştığından emin olmak hala iyi bir fikirdir. "bodyPart" ve "fromPart" fonksiyonlarını aşağıdaki gibi tanımlayın:

bodyPart bookTitle = "Thanks for buying " ++ bookTitle ++ ".\n"
fromPart author = "Thanks,\n"++author

Şimdi bunları test de edebilirsiniz:

GHCi> bodyPart "Learn Haskell"
"Thanks for buying Learn Haskell.\n"
GHCi> fromPart "Will Kurt"
"Thanks,\nWill Kurt"

Herşey iyi görünüyor. Şimdi o fonksiyonların tümü birlikte bağlayacak bir fonksiyona ihtiyacınız var(createEmail fonksiyonunu tanımlama):

createEmail recipient bookTitle author = toPart recipient ++
bodyPart bookTitle ++
fromPart author
Bu üç fonksiyon çağrısının hizalanma şekline dikkat edin. Haskell, önemli olan beyaz boşlukları sınırlı kullanır (ancak Python kadar yoğun kullanan bir dil yoktur). Bu metinde herhangi bir biçimlendirmenin kasıtlı olarak ya da bilerek yapıldığını varsayın; eğer kod bölümleri dizilmiş olsaydı, bu bir sebepten dolayıdır. Çoğu editör bunu bir Haskell eklentisiyle otomatik olarak işleyebilir.

Tüm yazılı fonksiyonlarınızla, "createEmail" fonksiyonunu aşağıdaki gibi test edebilirsiniz:

GHCi> createEmail "Happy Reader" "Learn Haskell" "Will Kurt"
"Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

Fonksiyonlarınızın her biri beklendiği gibi çalışıyor. Şimdi onların tümünü birlikte aşağıdaki gibi "main" in içerisine yerleştirebilirsiniz(Temiz bir main ile first_prog.hs'i geliştirme):

main = do
print "Who is the email for?"
recipient <- getLine
print "What is the Title?"
title <- getLine
print "Who is the Author?"
author <- getLine
print (createEmail recipient title author)

Hepsini derlemeye hazırlanmalısınız, ancak aşağıdaki gibi her zaman onları ilk olarak GHCi'de test etmek iyi bir fikirdir:

GHCi> main
"Who is the email for?"
Happy Reader
"What is the Title?"
Learn Haskell
"Who is the Author?"
Will Kurt
"Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

Tüm parçalarınız birlikte çalışıyor gibi görünüyor ve beklendikleri gibi çalıştıklarından emin olmak için de onların her biriyle ayrı ayrı oynayabildiniz:

$ ghc first_prog.hs
[1 of 1] Compiling Main ( first_prog.hs, first_prog.o )
Linking first_prog ...
$ ./first_prog
"Who is the email for?"
Happy Reader
"What is the Title?"
Learn Haskell
"Who is the Author?"
Will Kurt
"Dear Happy Reader,\nThanks for buying Learn Haskell.\nThanks,\nWill Kurt"

İlk başarılı Haskell programınızı henüz tamamladınız. Kavradığınız temel iş akışınızla, artık Haskell'in muhteşem dünyasına dalabilirsiniz.

ÖZET

Bu derste amacımız, Haskell'e giriş yapmaktı. Bu kitap boyunca kullanıyor olacağınız araçları birlikte paketleyerek bünyesinde barındıran Haskell Platformu'nu yükleyerek başladınız. Bu araçlar: Haskell'in derleyicisi GHC'yi; Haskell için etkileşimli bir yorumlayıcı olan GHCi'yi; ve daha sonra bu kitapta kullanacağınız bir inşa etme(build) aracı olan Stack aracını içerir. Bu dersin geriye kalanı ise; Haskell programlarını yazma, kodu yeniden düzenleyerek iyileştirme, onunla etkileşime girme ve derlemeyi kapsamıştır.

Soru - 1: GHCi ortamında, 2'nin 123'üncü kuvvetini bulunuz?

Soru -2: "first_prog.hs" adlı dosya içerisindeki her bir fonksiyonda bulunan metni(string) düzenleyin, bunu yaparken onlarí GHCi içerisinde test edin ve son olarak da, yürütülebilir dosyanın "email" olarak adlandırılması için e-posta kalıp çıkarma programınızın yeni bir sürümünü derleyin.
 
Son düzenleyen: Moderatör:
Arkadaşlar kodlarda girinti kısımları yok ve biraz karışık duruyor, bu nedenle İngilizce'den Türkçe'ye tarafımca çevirdiğim PDF formatındaki dosyayı ilgi duyan arkadaşlar için buraya bırakıyorum:

Tahminimce başlangıç için değil, fakat yazılımı bilen birisi için çok yardımcı olacaktır, faydalı. 👌
Kitap tamamen giriş ve orta seviye grubuna yönelik, fakat daha sonradan taşların yerine oturması adına biraz ilerideki konulara yönelik atıfta bulunarak fonksiyonlara giriş yapılmış. Bu arada bu bilgiler şu kitaptan alınmıştır: Kitap Linki (Amazon)
 

Dosya Ekleri

  • Haskell-1.pdf
    90,1 KB · Görüntüleme: 10

Yeni konular

Geri
Yukarı