2023 olduğu için elbette Common Lisp öğreniyorum
(log.schemescape.com)- Gelecekte de kullanılabilecek bir dil üzerine düşünme sürecinde, bu seferki tercih pratiklikten çok eski ama modern bir geliştirme iş akışını bizzat denemeye daha yakın
- Janet, Lua ile benzer bir alanı hedefleyen küçük bir Lisp/Clojure tarzı betik dili; standart kütüphanesi ve makro tabanlı metaprogramlama özellikleri nedeniyle ilgi çekti
- Janet’in güçlü yanları gibi görünen taşınabilirlik, gömülebilirlik ve Parsing Expression Grammars, mevcut ihtiyaçlarla sınırlı ölçüde örtüşüyordu; CHICKEN Scheme, CLISP, SBCL ve Packrat gibi mevcut seçenekler de yeterli görünüyordu
- Common Lisp’in REPL odaklı geliştirme yaklaşımının özü, çalışma zamanı hata durumunda yığını, değişkenleri ve canlı verileri inceleyip düzelttikten sonra duraklatılmış yürütmeyi sürdürebilmek
- Yeni bir dil ve standart kütüphaneyi öğrenmenin maliyeti var; ancak bu iş akışının baştan tasarıma dahil edildiği bir ortamda bunu doğrudan deneyimlemeye değer olduğu sonucuna varıldı
İlginin Janet’ten Common Lisp’e kaymasının nedeni
- Gelecekte de kullanılabilecek kod yazmak istediği için future-proof programming languages üzerine düşünüyordu; ancak bir şey geliştirip paylaşırken popüler, taşınabilir ve kullanışlı bir dil seçen pragmatik bir tutum benimsiyor
- Öte yandan eğlence ve deney amacıyla başka programlama dillerine ve araçlara da bakıyor
- Yakın zamanda ücretsiz kitap Janet for Mortals ile karşılaşınca Janet ilgisini çekti
- Janet, Lua’ya benzer bir nişi hedefleyen, nispeten küçük ve Lisp/Clojure’dan esinlenmiş bir betik dili
- Gerçek bir standart kütüphane, Lisp tarzı metaprogramlama ve makrolar üzerinden derleme zamanında yürütme sunuyor
- Janet’te genel Lisp özelliklerinin dışında en çok ilgi çeken taraflar taşınabilirlik, gömülebilirlik ve Parsing Expression Grammars oldu
- Şu anda bir dili gömme ihtiyacı olmadığı için gömülebilirlik belirleyici bir neden olmadı
- Taşınabilirlik açısından CHICKEN Scheme, CLISP ve Steel Bank Common Lisp de iyi görünüyordu
- Ayrıştırma için Packrat makul bir seçenek gibi görünüyordu
- Sonuçta Janet’i mevcut Lisp seçenekleri yerine seçmek için nedenler azaldı
Common Lisp öğrenmeye yönelten REPL odaklı iş akışı
- Common Lisp’in neredeyse kendine özgü REPL odaklı iş akışını ele alan yazı öğrenme motivasyonu oldu
- Örnek senaryoda
foofonksiyonu, henüz tanımlanmamışbarfonksiyonunu çağırıyor vefooçalışırken hata oluşuyor- Eski Lisp veya Smalltalk ortamlarında
foonun durması bir breakloop’a yol açar - breakloop, duraklatılmış fonksiyonun dinamik ortamı içinde çalışan eksiksiz bir REPL’dir
- Duraklatılmış çağrı yığını içinde gezip her yığın çerçevesinde görünen değişkenleri kontrol edebilirsiniz
- Çalışmakta olan programın canlı verilerini inceleyip değiştirebilirsiniz
- Sorun hatalı bir değişken ya da alan değerinden kaynaklanıyorsa değeri değiştirip duraklatılmış fonksiyonu sürdürebilirsiniz
- REPL içinde eksik
barfonksiyonunu tanımlayıpfooyu sürdürerek makul bir sonuç elde edebilirsiniz
- Eski Lisp veya Smalltalk ortamlarında
- Daha önce de çeşitli “edit and continue” özellikleri görülmüştü; ancak Common Lisp’in bu tür bir iş akışı düşünülerek tasarlanmış olması yeni ve dikkat çekici geldi
- Önceki hata ayıklama deneyimi
printf debuggingden gerçek hata ayıklayıcı kullanımına doğru evrilmiştiprintf debugging, geçici olarak günlükleme kodu ekleyip yeniden derleyerek çalışma sonucunu kontrol etme yöntemidir- Programı değiştirme veya yeniden çalıştırma imkânı olmayan durumlarda hata ayıklayıcı öğrenmek gerekiyordu
- Gerçek hata ayıklayıcı doğru araçtır; ancak hata ayıklama ortamını kurmak zahmetli, hatta bazen imkânsız olabilir
- Common Lisp, hata ayıklamayı bir adım daha ileri taşıyan bir ortam gibi görünüyordu
- Hata ayıklayıcıda belleği değiştirerek olası düzeltmeleri deneme deneyimi vardı
- Ancak çalışan bir süreç içinde kodu yeniden yazıp yama yapabilmek özellikle çekiciydi
- Common Lisp öğrenmenin amacı, programları etkileşimli olarak inşa ederken bu yöntemin keyifli bir çalışma biçimi olup olmadığını bizzat görmek
- Yeni bir iş akışını keşfetmek için yeni bir dil ve standart kütüphane öğrenmenin en iyi seçim olup olmadığı kesin değil; ancak daha iyi bir alternatif de açıkça görünmüyor
- Başka dillerde de benzer REPL-editör entegrasyonları olabilir
- Ancak bu tür özelliklerin baştan desteklenmek yerine sonradan eklenmiş olabileceği, bu yüzden daha fazla hata içerebileceği düşünülüyor
- Memnun kalınmasa bile, standart araçlarla en iyi koşullarda denemiş olmak elde kalacak
- Common Lisp öğrenmeye yardımcı olan kaynaklar ayrı bir yazı olan Common Lisp resources içinde derlenmiş
1 yorum
Hacker News yorumları
Şimdiye kadar Common Lisp’te hoşuma giden şey, condition system’ın temiz olması ve restart’lar sayesinde kodun çok uzaktaki bölümlerini bile kolayca kontrol edebilmek. REPL odaklı geliştirme de ne olacağını tam bilmediğiniz durumlarda, örneğin yabancı bir veri kaynağını parse ederken, bağlamı kaybetmeden kodu düzeltip devam etmeyi sağladığı için kullanışlı.
Çok sayıda implementasyonu var ve birlikte çalışabilirliği de iyi; bir durumda hız için SBCL ile bellek kullanımını azaltmak için CLISP arasında geçiş yapabilmiştim. Lisp öğrenirken Scheme yerine CL tarafına yönelmemin nedenlerinden biri de birden fazla uyumlu implementasyonun olmasıydı.
Emacs acemisi olmama rağmen Common Lisp entegrasyonu harikaydı ve geliştirme için kullandığım çok yavaş netbook’ta bile iyi çalışıyor. Günümüzün hızlı bilgisayarlar, VS Code ve language server’lar çağında bu avantaj eskisi kadar büyük değil, ama kesinlikle retrofütüristik bir havası var.
Eksileri de var. En popüler paket yöneticisi QuickLisp fena değil, ama modern dil ekosistemlerinde alıştığımız özellik seviyesine ulaşmıyor. Dilin kendisi zamanda donmuş durumda olduğu için thread’ler, senkronizasyon, komut satırı argümanları gibi birçok şey için birlikte çalışabilirlik kütüphaneleri gerekiyor. Ayrıca SBCL’in, glibc kullanmayan Linux dağıtımlarına da binary dağıtabilmek için tamamen statik build desteklemesi gerçekten harika olurdu.
Kaynak kodu almak gibi kritik bir iş için şifreleme veya kimlik doğrulama desteklenmiyorsa, güvenliğe önem veren biri için QL’nin bir seçenek olması zor bence.
SBCL’in SourceForge’da barındırılması da ayrı bir sorun. SourceForge geçmişte indirme arşivlerine kötü amaçlı yazılım enjekte etmişti, bu yüzden bunu da bir güvenlik meselesi olarak görüyorum. Bugün hâlâ SourceForge kullanmayı gerektiren makul bir neden göremiyorum; önemli bir projenin neden hâlâ orada kaldığını pek anlamıyorum.
Lisp’i ve özellikle Common Lisp’i gerçekten seviyorum, ama bunlar yüzünden uzaklaştım; ayrıca bunları düzeltmeye yönelik pek ilgi de yok gibi görünüyor.
https://www.timmons.dev/posts/static-executables-with-sbcl.h...
https://www.timmons.dev/posts/static-executables-with-sbcl-v...
https://cons.io
Gerbil/Gambit Scheme, CL’ye alternatif olarak tamamen statik binary’ler üretebiliyor.
Normal bir Quicklisp kütüphanesi olarak da, anında başlayan bir core image olarak da, binary olarak da kullanılabiliyor. Script’ler de neredeyse anında çalıştırılabildiği için Babashka’ya benzeyen yönleri var. Hâlâ ayrıntılarını düzeltiyorum ve henüz v1.0 değil.
Blogdaki “runtime hatasını yeniden başlatmadan, bozuk kodu yerinde düzelterek ele alma” kısmı burada gösterilmeliydi: https://www.youtube.com/watch?v=jBBS4FeY7XM
Uzun ve ağır bir hesaplama çalıştırırken son aşamada hata olursa, en baştan yeniden çalıştırmak yerine etkileşimli debugger’a giriyor, hatalı satıra gidiyor, düzeltilmiş fonksiyonu compile ediyor, sonra debugger’a dönüp stack frame’in bir noktasından yürütmeyi sürdürüyoruz. Ardından programın geçtiğini görebiliyorsunuz.
M-ile REPL’e kopyalama yöntemi, Clojure’un sunduğutap>’ten çok daha iyi geliyor. Portal gibi araçlar kullanılsa biletap>bence süslenmiş bir pretty-print’ten ibaret.Performans açısından da Common Lisp statik tip belirtmeye izin veriyor ve SBCL bunu yaptığınızda çok verimli kod üretebiliyor. Kendi yazdığınız koda
DISASSEMBLEçalıştırıp gerçekten ne üretildiğini görmek ve optimize etmek faydalı. Özellikle SB-SIMD ve Loopus gibi paketler sayısal hesaplama uygulamalarında gerçekten büyük yardım sağlıyor.:quackanahtarı true ise ördek gibi davran” yaklaşımını iyi harmanlıyor; bu da entity-component-system tarzı oyun tasarımı için gayet uygun.Ama Common Lisp’in geliştirme tarzını ve bu dünyadaki condition system’ı son bir yıldır gerçekten özledim. CL’de o kadar yetkin bir hacker bile değilim, ama kısa sürede alışkanlık haline geldi. CLOS ile CL’nin primitive veri tipleri göründüğünden daha iyi uyum sağlasaydı keşke.
Cider’da bunu kesinlikle kolayca yapabiliyorsunuz ve diğer editörlerdeki başlıca araçlar da muhtemelen benzer şekilde yeterince iyi yapıyordur.
DISASSEMBLEbenzeri bir şeyi bir ölçüde yapabilirsiniz.https://github.com/Bronsa/tools.decompiler gibi yardımcı projeler var; OpenJDK’nin JitWatch’u da (https://github.com/AdoptOpenJDK/jitwatch) mevcut. Diğer JVM’lerde de benzer araçlar var
Lisp kadar sezgisel değil ama yine de mümkün
Çekici biçimde iyi çalışan, anlaşılması güç bir şey ortaya çıkarmış olmak güzel; ama artık maaşını sıfır sayısıyla ifade edebilen kişiler dışında kimse ona dokunmak istemeyecektir
https://www.digitalocean.com/community/tutorials/java-repl-j...
https://github.com/waf/CSharpRepl
https://pub.dev/packages/interactive
Ruby, Python, PHP, Lua bir yana, C++’ta bile hantal da olsa bir REPL var: https://github.com/root-project/cling
Lisp tarafında REPL’in artık ayırt edici bir unsur olmadığını her söylediğimde downvote alıyorum
https://github.com/kaveh808/kons-9
Ancak bu yeniden tanımlama ve üzerine yazmalar sonunda ad alanını kirletiyor; bir noktada bir şeyler bozuluyor ve sunucuyu yeniden yüklemek gerekiyor
Eksileri de çok, ama neredeyse tüm diğer dillerin ve geliştirme ortamlarının saf teknik eksilerinden daha kötü değiller. Python ve JS daha yaygın kullanılıyor ve kütüphaneleri de daha fazla, fakat geliştirme deneyiminin çok daha kötü olduğunu düşünüyorum. C#, TS, Py, Hs ve daha niş dillere kadar yıllar içinde çok şey kullandım; ama o dillerde tıkandığımda ya da açıkça kötü şeyler yüzünden ciddi biçimde sinirlendiğimde her seferinde CL(SBCL + Emacs + Slime)’a dönüyorum. O zaman içim rahatlıyor ve dünyada hâlâ iyi şeyler olduğuna dair güvenim tazeleniyor
Şu anda aptalca TypeScript ile bootstrap ettiğimiz bir ürün için yatırım alma sürecindeyiz; çıkış sürümünü ise CL ile baştan yapmayı planlıyoruz. Bu da önümüzdeki 3-5 yıl boyunca lansmana kadar CL ile, CL içinde çalışacağım anlamına geliyor; DSL uygulama, kod üretimi, makro çalışmaları, statik tip çözümleyicisi uygulama gibi eğlenceli işler yapacağım için memnunum
C# ile çalışırken Lisp’te neyi özlüyorsun?
Benim uç sayılabilecek görüşüme göre fonksiyonel programlamanın yaygın biçimde parlayamamasının nedeni şu: Program yazmak için kesinlikle fena değil, ama günümüzde yazılımların çoğu “bilgisayarımda/sunucumda komut satırından çalışıp bitene kadar dönen program” değil; “başlayıp kullanıcı girdisine tepki veren ve kullanıcı kapatana kadar süren program” ya da “başladıktan sonra ağdan veya başka otomatik I/O’dan gelenlere yanıt veren, başka bir yazılım durmasını söylediğinde duran program”
Bu tür işler saf fonksiyonel tarzda yapmak çok daha zor; en azından benim denediğim, güçlü görüşlere sahip fonksiyonel uygulamalarda böyleydi. Çünkü artık yalnızca bir ifadeyi değerlendirmekten ibaret değil; durumu başlatmak, I/O’ya tepki vermek, durumu güncellemek veya ek I/O yapmak, izleme·alma·ek I/O·durum güncelleme için paralelleştirmeden yararlanmak söz konusu
Elbette Lisp ile bunları yapmak imkânsız değil; fakat lisans yıllarında birkaç dönem gördüğüm fonksiyonel programlama ve C++/Scala’da bu sorunları çözmek için profesyonel olarak fonksiyonel özellikler kullanma deneyimime göre, bu tür uygulamalarda fonksiyonel programlamayı iyi çalıştırmak oldukça zor geldi ve bu uyumsuzluk fonksiyonel programlamaya daha derinlemesine girmekte tereddüt etmeme yol açtı
Common Lisp’ten ayrı olarak, kod üreten kod yazmam gereken durumlar sık sık oldu. Bu alanda Lisp’in parlayacağını düşünüyorum ama henüz deneme fırsatım olmadı. Common Lisp’i düşünmeden önce yaptığım yakın tarihli proje örnekleri arasında, statik site üreticisinde belirli bir JSON Schema’yı doğrulayan kod üretmek ve bir programlama oyununun hikâye içeriğinde kullanmak üzere Markdown’dan JSX üretmek var
O zamana kadar kullandığım sayısız C makrosunu saymıyorum bile
Başka tarzların iyi özelliklerinin hepsini de çöpe atmak gerekmez. Fonksiyonel programlamanın bazı kısımları genel amaçlı dillerde de giderek daha yaygınlaşıyor. Örneğin Rust da birçok durumda fonksiyonel kalıplar kullanır
Ayrıca Lisp, isterseniz nesne yönelimli ya da imperatif tarzda da kullanılabilir. Haskell değildir
Lisp’i başlangıç noktası, Haskell’i ise asıl mesele olarak görüyorum. Gerçek üretim kodu yazmasanız bile o tarzı öğrenmenizi öneririm
(let ((pair (cons 1 nil)))(setf (cdr pair) pair)(list (first pair) (second pair) (third pair)));; => (1 1 1)“Sonsuza kadar derlenmesini” istiyorsanız ANSI C ile yazın
“Sonsuza kadar hayatta kalmasını” istiyorsanız Python 2.7 ile yazıp tüm organizasyonun altyapı şablonlarının omurgası hâline getirin. Özel Ansible modülü olursa ekstra puan
Benim Lisp’ten beklentim, ıvır zıvırı ve boilerplate’i en aza indirerek ya da soyutlayarak düşünceleri koda dökmeyi kolaylaştırması. Bana göre Lisp ifadeleri düşünceleri ifade etmenin en doğal yolu gibi görünüyor
Daha çok GUI uygulamaları yazmakla ilgilendiğim için Clog öğrenmek ya da bir GUI toolkit’i üzerinde iyi bir wrapper bulmak veya geliştirmek istiyorum
(do-it (do-it first))(do-it first)düzgün çalışıyor ama(do-it (do-it first))çağrısı başarısız olursa ne olur?Bozulan noktada kontrolü alıp
do-ittanımını düzeltebilsem güzel olurdu. Ama düzeltirsem(do-it first)sonucunun da değişmesi gerekirO zaman makinenin o anda bulunduğu nokta, mevcut kodla artık ulaşılamayacak bir nokta hâline gelir
Devam etmeyi mümkün kılan bir değişiklik yaptığınızda, o değişikliği yapabilmek için kontrolü aldığınız durumun kendisi değişiyorsa bunun nasıl çalıştığını gerçekten bilmiyorum