Homoiconic Python Dili
(aljamal.substack.com)- Lisp’in homoiconic özelliği, kodu ve veriyi aynı biçimde ele alma niteliğidir; klasik “Lisp in Lisp” Python’a taşındığında bu fikir daha tanıdık bir söz dizimi içinde görünür hale gelir
- Orijinal Lisp, kod temsili olan M-expression ile veri temsili olan S-expression’ı birlikte barındırıyordu; “Lisp in Lisp” ise M-expression ile S-expression Lisp’i uygular
- Python sürümü, S-expression’ı Python listeleri olarak temsil eder; M-expression ise fonksiyon çağrılarına ve koşul ifadelerine taşınarak ayrı bir parser olmadan bir interpreter kurulmasını sağlar
- İlk interpreter
atom,eq,car,cdr,cons,appendgibi liste primitive işlemleri üzerine kuruludur;lambdadesteği içinassoc,pairlisve ortam listesi eklenir eval, ifadeyi ve ortamı birlikte alarak değişken bağlamalarını işler;pairlisveassocaracılığıyla argümanları ve değerleri dinamik kapsam yaklaşımıyla eşler
Lisp’in gösterdiği kod ve veri birleşimi
- Lisp, 1960’ların başında John McCarthy’nin Lisp paper ve Lisp 1.5 manual üzerinden onlarca yıl sonra bile geçerliliğini koruyan birçok fikir bıraktı
- Bunların merkezinde homoiconic özellik yer alır
- Genel dillerde kod, veri üzerinde işlem yapan operasyonların dizisi olarak anlaşılır
- Lisp, kodu ve veriyi aynı biçimde ele alarak operatör ile operand arasındaki sınırı bulanıklaştırır
- Alan Kay, Lisp 1.5 manual’ın 13. sayfasının altındaki “Lisp in Lisp” kodunu “Maxwell's Equations of Software” olarak adlandırdı
- Birkaç satır kodun tüm programlama dünyasını içinde barındırdığı için büyük bir aydınlanma olduğu yönünde bir alıntı yer alır
“Lisp in Lisp”i Python’a taşıma biçimi
- Amaç, klasik “Lisp in Lisp” kodunu Python ile yeniden yazarken orijinal kodun ruhunu mümkün olduğunca korumaktır
- Lisp’in iki tür sözdizimsel temsili vardır
- M-expression: kod temsili, meta’nın kısaltması
- S-expression: veri temsili, symbolic’in kısaltması
- İki temsil anlamsal olarak eşdeğerdir
- Mevcut “Lisp in Lisp” kodu M-expression ile yazılmıştır ve S-expression Lisp’i uygular
- Python uygulamasında Lisp S-expression’ları Python listeleri ile temsil edilir
- Lisp, “List Processing”in kısaltmasıdır ve liste adlı tek bir veri yapısı etrafında çalışır
- Python listeleri, Lisp S-expression’larını emüle etmek için uygun bir temsil olarak kullanılır
- M-expression, Python’un fonksiyon çağrıları ve koşul ifadeleri gibi kod yapılarına çevrilir
- Bu eşleme sayesinde string manipülasyonu veya ayrı bir parser uygulaması olmadan interpreter yapılabilir
Liste primitive işlemleriyle yapılan ilk interpreter
- Lisp uygulaması için dilin dışından sağlanan bazı temel fonksiyonlara ihtiyaç vardır
- Python uygulamasında kullanılan liste primitive işlemleri şunlardır
atom(x):x’in liste olup olmadığını kontrol edereq(x,y):xiley’nin aynı olup olmadığını kontrol edercar(x): listenin ilk öğesicdr(x): listenin geri kalanıcons(x,y): bir atomu listeye eklerappend(x,y): iki listeyi birleştirir
- Birkaç özyinelemeli primitive işlem dışında, Llama3-70b Groq üzerinde kullanılarak “Lisp in Lisp” kodunun bir alt kümesini çalıştıran bir interpreter hızlıca oluşturulabildi
- Örnekte Python listeleri S-expression gibi davranır
- Kodun tamamı github gists üzerinde sunulmuştur
lambda ve özyineleme için genişletme
- İlk uygulamada önemli bir özellik olan
lambdaeksiktirlambda, Lisp’te anonim fonksiyon tanımlamanın ve çağırmanın başlıca yoludur- Lisp’te
lambdaolmadan özyineleme uygulanamaz - Özyineleme olmadan, hesaplanabilir her şeyi hesaplayabilmenin asgari ölçütü olan Turing tamlığına ulaşılamaz
lambdaeklemek içinassoc(x,y)vepairlis(x,y)eklendiassoc(x,y), listeyle uygulanmış anahtar/değer aramasıdır ve association list kullanırpairlis(x,y), Python’dakizip(x,y)gibi iki listeyi eşler
- Orijinal Lisp’te basit bir doğrusal tarama bile özyineleme ile yapılmak zorundaydı
- Çünkü orijinal Lisp’te döngüler yoktu
- Python çevirisinde
assocvepairlis, list comprehension ile daha kısa ifade edilebilir
CONDişlemede orijinal Lisp’tekievcondöngüye çevrilir;LAMBDAişlemede deevlisiçin aynı yöntem uygulanır
Ortam listesi ve dinamik kapsam
- Orijinal Lisp’in
evalfonksiyonu iki argüman alır- İlk argüman, değerlendirilecek S-expression’dır
- İkinci argüman, anahtar/değer listesinden oluşan ortam listesidir
- Ortam,
LAMBDAişlemede değişken bağlamalarını korur- Fonksiyonda
xdeğişkeni varsa ve veri atanırsa,pairlisxsembolü ile veriyi eşler - Eşlenen değer ortam listesinde saklanır veya listeye eklenir
xgerektiğindeassocortamda arar ve ifadeye yeniden yerleştirir
- Fonksiyonda
- Bu bağlama biçimi dinamik kapsam olarak adlandırılır
- Nihai uygulama, orijinal “Lisp in Lisp”in Python’a taşınmış biçimidir ve son örnekte
lambdaçalıştırmayı da içerir
2 yorum
Hacker News yorumları
Basit ve hızlı, Python benzeri betik yazımı için bir Lisp ile ilgileniyorsanız, Clojure tarzı diller olan Hy ve Janet'e bakmaya değer
Hızlı başlamak için bir binary olarak sunuluyor ve günlük işler için kütüphaneler de içeriyor
CL↔Python tarafında https://github.com/CodyReichert/awesome-cl?tab=readme-ov-fil... bağlantısına da göz atmaya değer
Ben Hy maintainer'ıyım; anlamlı bir performans farkı görülüyorsa bu bir bug'dır
M-expressions bu tuzaktan kaçındığına göre, Lisp'in homoiconicity'sini ve kavramsal zarafetini korurken böyle parantezleri gerektirmeyen gerçek bir dil olup olmadığını merak ediyorum
Tam olarak düşündüğünüz şey. Açıklaması da son derece doğrudan: “Program in YAML”
Neredeyse daha fazla bir şey söylemeye gerek yok
Bir dakika, bu sadece Python ile yazılmış bir Lisp değil mi?
Başlığın ima ettiği gibi homoiconic bir Python gibi görünmüyor; benim kaçırdığım bir şey mi var?
Bunun için Python sözdiziminin tuple, liste ve dictionary'lerden oluşması ve yorumlayıcının bunları doğrudan değerlendirmesi gerekirdi
Python ile kısa ve öz biçimde uygulanabilen bir başka işlevsel dil de Binary Lambda Calculus
Kodun önemli bir kısmı BLC'nin saf girdi/çıktı modelini ele alıyor; değişken araması için de ilişki listesi yerine çevre dizisini de Bruijn indeksleriyle indeksliyor
Aynı sayfada başka 9 dilde uygulama da var ve BLC'nin self-interpreter'ı, parser ve tokenizer dahil, 232 bit (29 bayt) ile en kısa olanı
[1] https://rosettacode.org/wiki/Universal_Lambda_Machine#Python
MIT'nin programlamaya giriş dersi, tüm öğrencilere Python ile bir Lisp yorumlayıcısı yazdırıyor
Bu, dersin bir zamanlar gerçekten Lisp ile verildiği dönemin izi
https://py.mit.edu/spring24
Benzer bir şeyi JS listeleriyle yapmıştım: https://github.com/andrelaszlo/js-lisp
Modern dillerin onlarca yıl sonra Lisp’in şaşırtıcı özelliklerini yeniden keşfetmesi hem komik hem ironik
Birkaç gün önce, ev sunucumdaki API’yi 9 saat boyunca çağıran bir Python programı durdu ve buna sinirlendim. Benzer API çağrıları yapıyordu; önceden tanımlanmış prompt şablonları ve dilbilgisi kısıtlarıyla bir LLM çağırıyordu
Kalan yinelemeleri ayrı çalıştırmadan önce programın durumunu kaydedip çıkmak istedim, ama çalışan Python kodunu değiştirme ya da değişkenleri inceleme yolunu bulamayınca sonuçta 9 saatlik işi kaybettim
Birkaç gün sonra https://malisper.me/debugging-lisp-part-1-recompilation/ yazısını gördüm ve Common Lisp’in bu tür özellikleri onlarca yıl önce dile gömmüş olmasına şaşırdım. En güçlü makro sistemi gibi diğer özellikler için de aynısı geçerli
Ama sorun da bu. İfade gücü fazla yüksek
Lisp bana hep karma medya görsel sanatlarını hatırlattı; ifade özgürlüğü kulağa hoş geliyor ama sonuçta daha geleneksel tek mecralı sanatlara göre ortaya çıkan işler çoğu zaman daha zayıf kalıyor. Bu da mecranın getirdiği kısıtların da ifade gücü kadar önemli olduğunu gösteriyor
Ya da https://github.com/malor/cpython-lldb de var
Daha fazla yöntem için https://github.com/albertz/pydbattach/
Bu uygulama nasıl homoikonik Python oluyor?
Program büyüdükçe Lisp’in okunmasını zorlaştırma eğilimini bir ölçüde kontrol eden bir tip sistemi olan bir Lisp var mı?
Tip sistemi olmasa bile, bu özelliği dizginleyebilecek başka unsurlar olup olmadığını merak ediyorum
Metaprogramlama gerçekten çok havalı, ama bazen şimdiye kadar gördüğüm en anlaşılmaz soyut Haskell gibi okunuyor ve yol gösterecek bir tip imzası bile yok
Tip sistemi ve linter’ların kodu otomatik olarak hizaya sokmak için en iyi araçlar olduğunu düşünüyorum, ama Lisp seçme nedeninin kendisini kısıtlamadan bir Lisp projesinin bu eğilimlerini nasıl bastırabileceğimizi pek gözümde canlandıramıyorum
Fiilen standarda en yakın olan SBCL, derleme zamanı tip kontrolünde de oldukça iyi
Örneğin
(declare (type String a b))sonrasında(+ a b)yazarsanız,Aiçin türetilmiş tipinSTRINGolduğunu amaNUMBERolması gerektiğini söyleyen bir uyarı yakalıyorScheme’den CL’ye geçmemin en büyük nedeni bu tip kontrolüydü; sonrasında da restarts ve continuable asserts gibi küçük ama güzel özellikler yüzünden kaldım
İfade odaklı Python olsaydı, bugünkü Python’dan çok daha iyi olurdu
Bu yazı başlığın ima ettiği şeyi anlatmıyor ama Lisp’i açıklamak açısından iyi
Yine de sorun değil, ama “ifadeleri ikinci sınıf sözdizimi olarak tutmak” oldukça temel ve bilinçli bir seçim
Hatta parantez çorbası yazmak isteyen geliştiricileri zorlamak için yapılmış bir tasarım da denebilir
Bu yüzden
lambda:ve:=kullanımını caydırmak için bilerek hantal bırakılmış, ayrıca anonim function da yazılamıyorPython, akıllıca işler yapmak istiyorsanız callable object değil iterator kullanın diyen bir dil
Her şey için Lisp tarzı söz dizimsizlik olmasını isterdim
Bana göre bu daha iyi ve neyse ki CL’de bunu yapmak kolay
Python içine gömülü bir Lisp lehçesi
https://hylang.org/