Fonksiyonel Programlama Notları 2: Monad'ları Anlamaya Çalışmak
01 Nov 2013Uzun zamandır Fonksiyonel programlama ile başetmeye ve anlamaya çalışıyorum. Bu yolculuk beni matematiğe kadar indirdi ve oldukça da zevkli geçiyor. Kafamı karıştırsa da oldukça zevkli. (Geçen hafta NTV Yayınlarının “Sayılar” isimli kitabını alıp okumaya başladım, matematik o kadar tatlı geldi ki…)
Bu serüvende karşıma anlaşılması zor olan bambaşka bir yapı çıktı: Monadlar. Bir önceki yazımda bu konuya değinmiştim, fakat bu konuyu biraz daha geliştirme ihtiyacı hissettim.
Konuyla alakalı olarak geçenki yazımdaki kısım tam olarak şu şekildeydi:
Fonksiyonlar kendi ihtiyacı olan yan etkileri parametre olarak alıp müdahale ediyorlar, böylece yan etki yaratmadan aynı işlemleri yapabiliyoruz. Yani global state’de bir şey tutmadan bir state’i taşıyabiliyoruz.
function f(x, y) { return [2*x + 5, y]; } diye bir fonksiyonumuz olduğunu varsayalım;
function g(z, y) { return [z+10, y]; } diye de başka bir fonksiyonumuz var;
y’nin taşımamız gereken global bir state olduğunu düşünelim;
g.apply(null, f(2, 5)) //=> [19, 5] Bu durumu biraz irdeleyelim;
f(2, 5) //=> [9, 5] g(9, 5) //=> [19, 5] görüldüğü gibi y değerinin state'ini taşıdık, ve yanetki kullanmadık.
Bu konuyu anlamamda ssg’nin ekşisozlük’te yazdığı bu entry epey faydalı oldu. Şimdi onun verdiği örneği JavaScript ile yazalım;
Normalde yan etki kullanarak bir toplama fonksiyonu yazalım;
var genelToplam = 0; function topla(x, y) { var toplam = x + y; genelToplam += toplam; return toplam; } topla(2, 3); //=> 5 topla(4, 5); //=> 9 genelToplam; //=> 14 Şimdi bu yanetkiye sahip fonksiyonu yan etkisiz hale getirelim;
function topla(x, y, genelToplam) { var toplam = x + y; genelToplam += toplam; return [toplam, genelToplam]; } function genelToplam() { return topla(2, 3, topla(4, 5, 0)[1])[1]; } genelToplam() //=> 14
JavaScript’e Uyarlamak
Üstteki kodun epeyce anlaşılmaz olduğu kesin, zaten yazarken içime sinmemişti.
Şimdi bunu biraz daha JavaScript gibi düşünelim:
Bir toplama işlemi fonksiyonu yapsaydık:
şeklinde bir değer elde edecektik. Daha sonra yeni bir işlem için;
şeklinde bir yapı sağlayacaktık. Sürekli bir state tutuyoruz ve bu state üzerinden yürüyoruz. Şimdi de daha fonksiyonel şekilde yazmaya çalışalım:
Bu da doğru, fakat hala yetersiz ve state kullanmak istemiyorsak pek de kullanılabilir değil. Şimdi de bu fonksiyonu daha “monad” gibi ve fonksiyonel olarak implemente etmeye çalışalım.
Hatırlayacaksınız ki fonksiyonel programlamada işler hep küçük, saf fonksiyonlar halinde uygulanıyor.
Hiç yanetki oluşturmadan state kullanmamız gerektiren bir durumu kullanarak değeri taşıdık.
Şimdi de bunu biraz daha JavaScript gibi yapalım, JavaScript’te eğer bir nesne değeri alınmaya zorlanıyorsa valueOf
methodu çağırılır.
diyerek daha kullanılabilir bir hale getirebiliriz.
veya
şeklinde kullanabiliriz.
Monad Kanunları
Haskell’de Monad kanunları mevcutmuş, bu kanunlara uymayanlar monaddan sayılmıyormuş; madem öyle; inceleyelim:
Yani diyor ki;
Yani Matematiksel olarak;
kurallarını sağlamalı…
Bu kuralları sağlamak içinse şunlar gerekli:
- İki method implementasyonu olmalı:
bind
vereturn
- Üç kurala uymalı: left identity, right identity, associativity
Şimdi bu Monad’ımızı bu şartları sağlayarak tekrar implemente edelim:
bind
methodunu chaining yapmak için implementasyonumuza ekleyelim:
toplam
fonksiyonumuz zaten return değerini sağlıyor.
Left Identity:
Tüm x ve fn’ler için:
Right Identity:
Tüm x’ler için:
Associativity
Tüm x, fn ve gn’ler için
Bu kuralları da kitabına uydurduğumuza göre, artık legal bir monad’a sahibiz demektir. :)
Kaynaklar
Konuyla alakalı okuduğum kaynakları hemen okuduktan sonra Twitter adresimde paylaşıyorum, oradan takip edebilirsiniz. http://twitter.com/fkadev