http://discuss.ocaml.org 'u açıyorum; uzun zamandır portfolyomu fonksiyonel yaklaşımlar ve diller konusunda zenginleştirmekteyim ve de aktif bir iş arayışındayım. İnternette bir OCaml ilanı var ise orada bulabileceğimden eminim.
Komşu başlıklardan birisinde "Closure’da REPL Driven Development" yapmanın ne kadar keyifli olduğunu, acaba OCaml’da da bunun mümkün olup olmadığı tartışılıyor.
I want to define “Repl based development” not as “occasionally use the REPL”, but something like this:
we start a REPL we type code in some editor, and continuously “send last expression” to the REPL, and we look at the new output eventually, we clean up the buffer a bit and store it as code
REPL Driven Development ne?
Piyasada benim bilmediğim yeni bir moda akımı mı var?
Derhal araştırmaya koyuluyorum…
Read-Eval-Print-Loop
Herhangi bir "interpreted" dil ile kod yazmış iseniz, adina "REPL" dendiğini bilmeseniz de bir REPL arayüzüne denk gelmişsinizdir.
REPL programı:
-
Kullanıcıdan bir satır kod alır (Read)
-
Sonucunu hesaplar (Eval)
-
Ekrana yazar (Print)
-
1’inci adıma dönerek sonsuz bir döngü oluşturur (Loop)
REPL programı (ctrl-c veya ctrl-d ile) sonlandırılıncaya kadar bu döngüde kalarak girdilerinizi hesaplar. Ama ben bu sistemi hesap makinesinden öte bir şey icin henüz kullanabilmiş değilim.
REPL ile Kod Geliştirmek
REPL’i yazılım geliştirme surecinin merkezine oturtmak icin JVM’e hapsolmuş bir LISP kullanmak zorunda değilsiniz (C dili icin bile REPL bulunabiliyor). LISP programcıları uzun bir süredir kodlarını (metodlarını) istedikleri şeyi yapana kadar REPL’de geliştirip sonra da bir dosyaya kaydediyorlar. npm install mocha
demeden, kalite mühendisleri tepelerinde dikilmeden doğal bir şekilde Test-Driven-Development yapıyorlar. Bunu yaparken de REPL ile entegre bir kod geliştirme ortamından faydalanıyorlar.
Bir yazılımcının hangi kod geliştirme ortamini kullanacağı tamamen şahsi bir tercihtir; aynen sert zeminli bir ofiste işini yapmaya odaklanmış 20 mühendisin arasından günde 20 defa geçeceğini bile bile yumurta topuklu ayakkabı giymek gibi.
Siz kod yazmak için:
-
Vim/NeoVim gibi bir metin düzenleyici kullanıyor olabilirsiniz,
-
NetBeans gibi bir IDE kullanıyor olabilirsiniz,
-
Emacs gibi bir işletim sistemi kullanıyor olabilirsiniz,
Ve de REPL’le entegre bir şekilde kod yazıp yazamayacağınız ortamınızın bunu destekleyip desteklemediğine bakar.
Ben birkaç hafta önce NeoVim'den Kakoune adında bir programa geçmiş, Kakoune’da :repl
komutuna denk gelip ne yaptığını anlamamıştım.
Kakoune, "kakuun" diye okunur. |
Kakoune/REPL Entegrasyonu
Bu arada Bash/Sh’nin de bir REPL olduğunu farkediyoruz |
Kakoune’da bu işi yapabilmemizi sağlayan iki komut var:
-
:repl
yeni bir pencere pencere açar -
:send-text
ise:repl
komutuyla açılmış pencereyi bulup, seçilen metni ona yollar
Kakoune’un pencereleri pencere yöneticisi seviyesinde (X11) veya Tmux’un içinde olabiliyor. Kakoune hangi ortamda çalıştığını anlayıp ona göre davranıyor.
Ben kakrc
map global normal <#> ":send-text" |
REPL destegiyle NodeJS yazmak
REPL’le nasıl konuşacağımı oğrendiğime göre, örnek bir problem çözerek REPL’i etkince kullanarak kod yazmayı denemek istiyorum. http://exercism.io 'da geldiğim son probleme bakalım:
Luhn doğrulama problemi
Kredi kartı vb. numaralarının geçerliligini bulmada kullanılıyor.+ Bir string alıp bunun Luhn algoritmasınca gecerli bir sayi olup olmadığını hesaplayacağız.
-
String’in boyu 1’den az olamaz
-
Boşluk olabilir ama hesaplamaya başlanmadan silinmelidir
mesela bize verilen metin şu olsun:
4539 1488 0343 6467
boşluklarını silelim:
4539148803436467
-
Bunlar haricinde rakam dışında bir karakter olamaz
-
En sağdan baslayıp her ikinci rakamı ikiyle carpın
-
Carpım sonucunda 10 veya daha büyük bir sayı bulduysanız 9 çıkarın
bu sayıyı elde ettik:
8569247803833437
-
Tum rakamları toplayın
sonuç
80
-
Sonuç 10 ile bölunebiliyor ise geçerli bir sayıdır, yoksa değildir
Sizi bilmiyorum ama, ben iç içe (ya da peş peşe )bu kadar if else
yazmak istemem. Hele de böylesine beni fonksiyonel yaz! diyen, once bunu yap, sonra bunu yap şeklinde bir algoritma var ise Maybe
kullanmak isterim.
Maybe haznesi
Maybe; doğrulugundan emin olmadığınız değerleri içerisine koyup, uzerinde işlem yapabileceğiniz bir hazne.
Önce bir örnek gösterip sonra açıklamasını yapacağım.
Bir metod yazalım, bu metod bir sayıyla çağırılmış olabilir, veya hiçbir sey verilmeden çağırılmış da olabilir. Biz bunu 3’le çarpıp 2 ekleyelim…
const foo = num => {
return Maybe.of(num) (1)
.map(x => x * 3) (2)
.map(x => x + 2) (3)
.withDefault(0) (4)
}
1 | num’un "truthy" olup olmadigina gore Just num veya Nothing 'imiz olacak |
2 | map komutu Just x’i `Just 3x yapar, Nothing’e ise dokunmaz |
3 | Just 3x+2 veya Nothing |
4 | 3x+2 veya 0 |
Maybe’ye koyduğunuz değerler ya Bir Sey olarak tutulur, ya da Hicbir Sey. Eğer Hicbir Sey'iniz var ise yapacak bir şey yok zaten. Ama eğer Bir Sey'iniz var ise o değeri metodlar vererek güncelleyebilirsiniz.+
|
|
JavaScript’te Maybe ve benzeri fonksiyonel tanımları içeren kutüphaneler mevcut ama kendimizinkini yazmak da 2dk’mızı alacak.
class Maybe {
static of(x) { return x
? new Just(x)
: new Nothing() }
}
class Just extends Maybe {
constructor(x) { super();
this.$value = x }
map(f) { return new Just( f(this.$value) ) }
chain(f) { return f(this.$value) }
withDefault() { return this.$value }
toString() { return `Just ${this.value}` }
}
class Nothing extends Maybe {
map() { return this }
chain() { return this }
withDefault(x) { return x }
toString() { return "Nothing" }
}
NodeJS REPL’ine test değerleri yolluyorum…
Unutmayayım diye bu denemeleri de koduma kopyalıyorum. İstediğim sonucu veriyor mu diye de kontrol edeceğim. Bunları koduma koyduğum için de bu kod yaşadığı sürece bu test yapılıyor olacak.
const fail = msg => { throw new Error(msg) }
new Just(4).map(x => x + 2).withDefault(0) == 6 || fail("Just map withDefault")
new Nothing().map(x => x + 2).withDefault(0) == 0 || fail("Nothing map withDefault")
Maybe’ye dair yapmak istediğim 1 adet şey kaldı.
Maybe.of
Maybe’nin içerisine değer koymak için ideal bir yöntem değil. Bir değerin truthy
olup olmamasından ötesiyle ilgileniyor olabiliriz. Onun için yardımcı bir metod yazacağım:
const safe = pred => x => (pred(x)
? new Just(x)
: new Nothing())
safe(x => x > 5)(10) // Just 5
safe(x => x > 5)(0) // Nothing
(Maybe.of
metoduna da artık ihtiyacım kalmadı)
Kategori Teorisi konusunda bu tip dönüşümlere ayrıça değineceğim, ama şimdilik pratikte şu ikisinin farkını anlamanız şimdilik yeterli: map vs chain
|
Luhn doğrulama - devam
Bundan sonrası oldukça basit. Tüm doğrulama adımlarını sırasıyla ekleyeceğim.
Koduma feature’lari ekledikçe onları REPL’e yollayarak test ediyorum. Bu test davranışlarını da koduma kaydediyorum.
Sonuç
const sumList = l => l.reduce((a, b) => a + b, 0)
const luhn = str => (
new Just(str)
.map(x => x.split(""))
.map(l => l.filter(x => x != " "))
.map(l => l.map(x => parseInt(x)))
.chain(safe(l => !l.includes(NaN)))
.chain(safe(l => l.length > 1))
.map(l => l.reverse())
.map(l => l.map((x, i) => i % 2 == 1
? (x * 2)
: x))
.map(l => l.map(x => x >= 10
? (x - 9)
: x))
.map(sumList)
.map(x => x % 10 == 0)
.withDefault(false)
)
luhn("4539 1488 0343 6467") == true || fail("valid num")
luhn("1") == false || fail("too short")
luhn("123 a 456") == false || fail("has unknown chars")
sumList([1, 2, 3]) == 6 || fail("sumList 1 2 3")
Luhn Online REPL
Repl.it'i henüz keşfettim. Hem tüm kodu sizlerle paylaşmama, hem de sizlere kod ile oynama imkanı sunuyor.
Birkaç saat geçmiş, çoktan öğlen olmuş ve benim tek yapmak istediğim OCaml ilanlarına bakmak idi…
"sonraki sefere artık" diyerek öğle yemeğine çıkacağım
Etkin-calışma-suresi tutan bir komut satırı programı yazmaya baslayıp yolun sonunda yine NodeJS’te Smalltalk’taki gibi ileti al-ver arayüzü inşaa ederken bulacagimiz bir sonraki yazıda görüşmek üzere…