2 puan yazan GN⁺ 2024-05-13 | 2 yorum | WhatsApp'ta paylaş
  • 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, append gibi liste primitive işlemleri üzerine kuruludur; lambda desteği için assoc, pairlis ve ortam listesi eklenir
  • eval, ifadeyi ve ortamı birlikte alarak değişken bağlamalarını işler; pairlis ve assoc aracı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 eder
    • eq(x,y): x ile y’nin aynı olup olmadığını kontrol eder
    • car(x): listenin ilk öğesi
    • cdr(x): listenin geri kalanı
    • cons(x,y): bir atomu listeye ekler
    • append(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 lambda eksiktir
    • lambda, Lisp’te anonim fonksiyon tanımlamanın ve çağırmanın başlıca yoludur
    • Lisp’te lambda olmadan özyineleme uygulanamaz
    • Özyineleme olmadan, hesaplanabilir her şeyi hesaplayabilmenin asgari ölçütü olan Turing tamlığına ulaşılamaz
  • lambda eklemek için assoc(x,y) ve pairlis(x,y) eklendi
    • assoc(x,y), listeyle uygulanmış anahtar/değer aramasıdır ve association list kullanır
    • pairlis(x,y), Python’daki zip(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 assoc ve pairlis, list comprehension ile daha kısa ifade edilebilir
  • COND işlemede orijinal Lisp’teki evcon döngüye çevrilir; LAMBDA işlemede de evlis için aynı yöntem uygulanır

Ortam listesi ve dinamik kapsam

  • Orijinal Lisp’in eval fonksiyonu 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, LAMBDA işlemede değişken bağlamalarını korur
    • Fonksiyonda x değişkeni varsa ve veri atanırsa, pairlis x sembolü ile veriyi eşler
    • Eşlenen değer ortam listesinde saklanır veya listeye eklenir
    • x gerektiğinde assoc ortamda arar ve ifadeye yeniden yerleştirir
  • 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

 
GN⁺ 2024-05-13
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

    1. Hy(https://hylang.org/): Python bytecode'una derlenir, genelde Python'dan daha yavaştır ama tüm Python kütüphaneleriyle uyumludur
    2. Janet(https://janet-lang.org/): Lua tarzı çok hafif gömülü bir sanal makinedir, yaklaşık 1 MB boyutundadır, benzer işlemlerde Python'dan kabaca 2 kat hızlıdır ve C ile birlikte çalışması çok kolaydır
    • Hızlı Python benzeri betik yazımı için, piller dahil gelen bir Common Lisp olan https://github.com/ciel-lang/ciel de var
      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
    • Hy, Python'dan daha mı yavaş? En azından çalışma zamanında öyle olmamalı
      Ben Hy maintainer'ıyım; anlamlı bir performans farkı görülüyorsa bu bir bug'dır
      1. Basilisp(https://github.com/basilisp-lang/basilisp): “Python 3.8+ hedefleyen, Clojure uyumlu-ish bir Lisp lehçesi”
    • Bu biraz kapris gibi gelebilir ama Lisp, zorunlu saran parantezleri yüzünden bana hep itiraz davet eden bir dil gibi hissettirdi
      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
    • https://github.com/yaml/yamlscript
      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?

    • Greenspun'un onuncu yasasına göre evet: https://en.m.wikipedia.org/wiki/Greenspun's_tenth_rule
    • Ben de ekip arkadaşlarıma Lisp'i benimsetmek zorunda kalmadan kullanmamı sağlayacak bir homoiconic Python sanıp epey heyecanlanmıştım, ama değilmiş, sadece Lisp'miş
    • Öyle görünüyor. Bu homoiconic Python'u görmek isteyince meraklandım ama bunun mümkün olup olmadığından da emin değilim
      Bunun için Python sözdiziminin tuple, liste ve dictionary'lerden oluşması ve yorumlayıcının bunları doğrudan değerlendirmesi gerekirdi
    • Fark ne?
  • 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 şekilde, üniversitede aldığım bir derste de Lisp ile temel bir Lisp yorumlayıcısı uygulamamız istenmişti
  • 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

    • Modern dillerin onlarca yıl sonra Lisp’in özelliklerini yeniden keşfetmesi şaşırtıcı değil. Çünkü Lisp ailesi en esnek ve en ifade gücü yüksek dillerden
      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
    • Bir miktar durum incelemek için Py-spy veya Pystack kullanabilirsiniz
      Ya da https://github.com/malor/cpython-lldb de var
      Daha fazla yöntem için https://github.com/albertz/pydbattach/
    • Saatlerce çalışan bir süreçse, işi diske kaydetmeli ve kesildiği yerden devam etmeye hazır olmalısınız
    • Bir exception fırlatılmadı mı?
  • 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

    • Tip sistemi olan bir Lisp denince aklıma Common Lisp geliyor
      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, A için türetilmiş tipin STRING olduğunu ama NUMBER olması gerektiğini söyleyen bir uyarı yakalıyor
      Scheme’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
    • Daha dost canlısı ve erişilebilir tarafta Typed Racket, güçlü ama karmaşık tarafta ise Shen var
    • https://en.wikipedia.org/wiki/Dylan_(programming_language)
  • İ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

    • İfade odaklı Python artık Python olmazdı
      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ıyor
      Python, 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

 
kayws426 2024-05-13

Python içine gömülü bir Lisp lehçesi
https://hylang.org/