- Bu yazı, mülakat ortamında bir adayın basit bir FizzBuzz problemini lambda hesabı tabanlı saf fonksiyon kombinatorleriyle çözmeye çalıştığı hicivli bir anlatı sunuyor
- Baş kahraman JavaScript ile başlıyor, ancak giderek S, K, I kombinatorlerini tanımlayarak dilin temel yapısını kendi başına yeniden kuruyor
- Ardından boolean'ları, sayıları, listeleri ve string'leri tamamen kombinatorlerle ifade ediyor ve Y kombinatorü aracılığıyla özyinelemeyi kuruyor
- Sonunda FizzBuzz'ı tamamen point-free tarzda tamamlıyor, ancak kod insanın anlayamayacağı ölçüde büyüyüp genişliyor
- Yazı, programlama dillerinin özü ile soyutlamanın uç noktalarını mizahi bir şekilde incelerken, geliştirici kültürünün öz-hicvini de ortaya koyuyor
Mülakatın başlangıcı: FizzBuzz ve kombinatorler
- Hikâye, mülakatçı Dana'nın adaya FizzBuzz problemini çözmesini istemesiyle başlıyor
- Aday, sıradan bir döngü yerine S ve K kombinatorlerini tanımlayarak çözmeye başlıyor
- Dana şaşırıyor, ama aday “biraz daha sürer” diyerek devam ediyor
- Aday, I, B, C, W, T gibi çeşitli kombinatorler tanımlıyor ve bunları yeni bir dilin temel yapı taşları olarak kullanıyor
- Her kombinator, fonksiyon bileşimi, argüman değiştirme, kendine uygulama gibi lambda hesabının temel kavramlarını gerçekleştiriyor
- Kod yorumlarında “Bluebird, Cardinal, Warbler, Thrush” gibi kombinator adlarının takma adları da geçiyor
Boolean ve sayıların tanımı
- Aday, TRUE, FALSE, NOT ifadelerini yalnızca kombinatorlerle tanımlayarak boolean mantığını kuruyor
- Dana “bu lambda hesabı mı?” diye soruyor, ancak aday “lambda hesabı fazla şişkin” diye yanıt veriyor
- Ardından ZERO, SUCC, PRED, IS_ZERO gibi tanımlar yaparak sayı sistemini (Church numeralleri) kuruyor
SUCC ardıl, PRED öncül, DECREMENT ise 0'ın altına inmeyen azaltma işlemini gerçekleştiriyor
ONE'dan TEN'e kadar sayılar sırayla tanımlanıyor
Özyineleme ve Y kombinatorü
- Aday, ADD fonksiyonunu tanımlıyor ve bunu giderek point-free biçime dönüştürüyor
ADD_MAKER tanımlanıyor ve özyinelemeyi mümkün kılmak için bunun etrafına Y kombinatorü sarılıyor
- Dana, “JavaScript'te de özyineleme var” diye itiraz ediyor, ancak aday “yakında artık JavaScript olmayacak” diye cevap veriyor
- JavaScript'in katı değerlendirmesi (eager evaluation) nedeniyle stack overflow oluşunca aday kodu Skoobert adlı “tembel (lazy)” bir JS yorumlayıcısında çalıştırıyor
- Sonuçta başarıyla
3 çıktısı alınıyor
Aritmetik, listeler ve string'ler
- Aday, SUBTRACT, MULTIPLY, MOD, DIVIDE gibi tüm aritmetik işlemleri de kombinator tabanlı olarak kuruyor
- Her işlem,
IS_ZERO, PRED, SUCC gibi yapıların özyinelemeli birleşimleriyle oluşturuluyor
- Ardından CONS, FIRST, REST, EMPTY tanımlarıyla liste yapısını kuruyor
MAP, FOLD, RANGE, CONCAT gibi yüksek dereceli işlevsel işlemler de tamamen kombinator biçiminde uygulanıyor
- Listeyi string olarak yazdırmak için
renderList, showLines fonksiyonları yazılıyor
- Dana artık çoktan pes etmiş gibi görünüyor ve tepki vermiyor
Karakter ve string'lerin kurulumu
- Aday, karakter kodlarını 1'den 9'a ve ardından 10'luk birimler halinde kombinatorler kullanarak tanımlıyor
CHAR_A'dan CHAR_Z'ye ve CHAR_0'dan CHAR_9'a kadar her şey sayı kombinasyonlarıyla ifade ediliyor
ARRAY fonksiyonu aracılığıyla string'ler listeye dönüştürülüyor ve FIZZ, BUZZ, FIZZBUZZ oluşturuluyor
extractString fonksiyonu sayesinde liste gerçek string'e çevriliyor
- Konsol çıktısı
"fizzbuzz" oluyor
Sayıdan string'e dönüşüm
- Sayıyı basamak listesine çeviren
NUMBER_TO_DIGIT_LIST ve bunu string'e dönüştüren NUMBER_TO_STRING tanımlanıyor
DIVIDE, MOD, TEN kullanılarak her basamak ayrıştırılıyor
DIGITS_NUMERAL listesi aracılığıyla rakam-karakter eşlemesi yapılıyor
- Bu süreçle birlikte sayı → karakter → string dönüşümü tamamen kombinator sisteminin içinde mümkün hale geliyor
FizzBuzz'ın tamamlanışı
FIFTEEN, ONE_HUNDRED tanımlanıyor ve MAP ile RANGE kullanılarak 1'den 100'e kadar bir liste üretiliyor
- Her sayı için 3, 5 ve 15'in katları belirlenerek
"Fizz", "Buzz", "FizzBuzz" ya da sayı string'i yazdırılıyor
showLines(extractString)(FIZZBUZZ_RESULT) ile nihai sonuç ekrana basılıyor
- Dana, “şimdi memnun musun?” diye soruyor, ancak aday tüm değişken tanımlarını kaldırıp kodu yalnızca saf kombinatorlerle yeniden yazıyor
- Sonuç, binlerce satıra ulaşan ve yalnızca S ile K'den oluşan devasa bir ifade oluyor
Sonuç ve anlamı
- Yazı, programlama dillerinin temel yapı taşlarını incelerken aynı zamanda geliştiricilerin basit problemleri gereksiz yere aşırı karmaşık hale getirme eğilimini hicvediyor
- “Hiçlikten daha azıyla programlamak” başlığı, dünyayı dilin en küçük birimlerinden yeniden kurma girişimini simgeliyor
- Bu eser, fonksiyonel programlama, lambda hesabı ve kombinator mantığı üzerine derin bir kavrayışı mizah ve anlatıyla işleyen teknik bir edebiyat örneği
- Aynı zamanda “FizzBuzz'ı bile felsefi bir deneye dönüştüren geliştirici zihniyetini” hicivli biçimde ortaya koyuyor
1 yorum
Hacker News görüşleri
İnsanlar “bu da ne şimdi?” diye düşünüyor gibi göründüğü için combinator kavramının özünü açıklayayım
combinator, global durumu değiştirmeyen ve dış değişkenleri capture etmeyen bir fonksiyondur. Aynı argümanlar verilirse her zaman aynı sonucu üretir ve sistemin geri kalanını hiçbir şekilde etkilemez
Bu tür saf fonksiyonlar birleştirildiğinde yine başka bir combinator oluşur. Yani saf fonksiyonların bileşimi hâlâ saf fonksiyondur
Bu fonksiyonlar assembly ya da C ile de kolayca yazılabilir. Örneğin x64 üzerinde S ve K’yi doğrudan implemente edip, onun üstüne combinator tabanlı olarak Common Lisp derlerseniz, sonuçta Lisp sadece iki assembly fonksiyonunun üzerinde çalışıyor olur
Performansı pek iyi olmaz ama, sık kullanılan birkaç yüz kadar kalıbı inline combinator olarak isimlendirirseniz oldukça kullanışlı bir “makine” elde edersiniz
Bu yapı, mutation’dan çekinen Forth’a, bir bytecode VM’e ya da combinator’lara “komut” diyen bir CPU’ya da benzer
Sonuçta combinator, implementasyon ayrıntılarını mümkün olduğunca göz ardı ederek uygulamalı bilgisayar biliminin bir ifadesi olarak görülebilir. Bu yüzden lambda calculus’tan daha basit olduğu söylenebilir
Eğer lambda calculus’u birkaç combinator ile implemente edip onun üstüne Lisp kurarsanız, yığının en alt katmanında yapılması gereken makineye bağımlı işler son derece azalır
Bunu birkaç kez birleştirmeniz yeterli
“Ben lambda calculus falan asla kullanmam. Fazla şişirilmiş bir dil.”
Ama aslında combinatory logic daha da şişkindir. Aynı programı ifade etmek için daha fazla bit gerekir
Lambda calculus ise tam tersine en özlü dillerden biridir
JavaScript gibi eager dillerde bile argümanları dummy lambda’larla sararak lazy davranış elde edebilirsiniz. Örnek için bu JS BLC yorumlayıcısına bakın
İlgili makaleler için bu PDF ve IOCCC örneğine göz atabilirsiniz
uni.c) aslında lambda ifadelerini combinatory logic’e dönüştürerek hesaplama yapıyor. Sadece S ve K’den daha büyük bir temel küme kullanıyorlar“FizzBuzz biliyor musun?”
“Elbette. 10 karakterlik code golf sürümünü mü, junior’ın da anlayabileceği 12 satırlık sürümü mü, yoksa acayip bilgi gösterisi için yapılmış tamamen absürt sürümü mü?”
sonuç bağlantısı
combinator logic açıklaması gerçekten harikaydı. Yazım tarzı bana bu seriyi hatırlattı
Bir zamanlar okuduğum “FizzBuzz in TensorFlow” yazısını hatırlattı
bağlantı
Bu tür programlama mülakatı çerçevesi biraz yanlış geliyor
Mülakatın amacı (1) adayın programlama bilgisini ölçmek, (2) şirketin programlama kültürüne uyumunu görmek olmalı
Örneğin JavaScript pozisyonu için alım yapıyorsanız ve biri combinatory logic’e fazlasıyla dalmışsa, kültürel açıdan pek uygun olmayabilir. Bu yüzden elenmesi de garip olmaz
Moses Schönfinkel’i hatırlamanın zamanı geldi
combinatory logic’i 1920’de Hilbert’in öğrencisiyken icat etmişti. Rusya’ya döndükten sonra ruhsal hastalıklarla mücadele etti ve Moskova’da yoksulluk içinde öldü. Wikipedia’ya göre makaleleri komşuları tarafından yakacak olarak kullanılmış
Son kod bloğu, kaydırma çubuğu çıkacak kadar büyük (166KB)
S ve K, curried fonksiyonlardır; her seferinde yalnızca bir argüman alırlar
Ayrıntılar için bu StackOverflow yanıtına bakın
“Bluebird, cardinal, warbler, thrush” gibi kuş isimlerine bayıldım. Bunu yeni bir isimlendirme kuralı yapmak istiyorum
“Barendregt. Church artık fazla ana akım.”
“Yakında bu artık JavaScript de olmayacak.”
Sanki Douglas Adams, SICP anlatıyor gibi
“Bu... Y combinator mı?”
“Aynen. Özyineleme için gerekiyor.”
Eğlenceli bilgi: sabit nokta combinator’ları (fixed-point combinator) sonsuz tanedir. Yani Y combinator olmadan da özyinelemeyi implemente etmenin sonsuz sayıda yolu vardır