- Janet, küçük bir Lisp lehçesi olsa da buyurgan bir dil, birinci sınıf fonksiyonlar, tek tanımlayıcı ad alanı ve sözcüksel blok kapsamı sunarak sade bir başlangıç yapmayı mümkün kılar
- Çekirdek dil, yalnızca
do, def, var, set, if, while, break, fn olmak üzere 8 komuttan oluşur; makrolar ise daha güçlü veya daha kullanışlı kontrol akışı sarmalayıcıları oluşturur
- Dağıtılabilirlik, Janet programlarını Janet çalışma zamanına statik olarak bağlanmış yerel yürütülebilir dosyalar olarak derleyerek sağlanır; böylece kullanıcıların Janet ya da bağımlılık kurması gerekmez
- Parsing Expression Grammar (PEG), regex'ten daha basit, daha güçlü ve daha öngörülebilirdir; sh ise pipe ve redirection'ı Janet içinde ifade etmeyi sağlayarak CLI yazımının kapsamını genişletir
- Derleme zamanında yürütme, üst düzey komutları önce çalıştırıp ardından program durumunun anlık görüntüsünü diske kaydederek, makro olmadan da değerlerin, paylaşılan referansların, generator'ların ve closure durumunun çalışma zamanına taşınmasını sağlar
Sade bir çekirdek
- Janet buyurgan bir dildir ve birinci sınıf fonksiyonlara, tek tanımlayıcı ad alanına ve sözcüksel blok kapsamına sahiptir
- Dilin çekirdeği
do, def, var, set, if, while, break, fn olmak üzere 8 komutla küçük tutulur
- Makrolar, daha güçlü veya daha kullanışlı yüksek seviyeli kontrol akışı sarmalayıcılarını mümkün kılar
- Çalışma zamanı anlambilimi tanıdıktır ve standart kütüphane bütünüyle tek sayfaya sığacak kadar dilin geri kalanı da küçüktür
Yerel dağıtım ve gömme
- Janet programları, Janet çalışma zamanını statik olarak bağlayan yerel yürütülebilir dosyalara kolayca derlenebilir
- Dağıtımı alan kullanıcıların Janet, proje bağımlılıkları veya başka ayrı bileşenler kurması gerekmez
- Janet önce kendisini bytecode'a derler, sonra bu bytecode'u Janet çalışma zamanını başlatan bir
.c dosyasına yazar ve ardından sistemin C derleyicisiyle bu C dosyasını derler
- Basit bir “hello world” yerel binary'si 1MB'tan küçüktür; Janet 1.27.0 için aarch64 macOS üzerinde boyut 784K idi
- Bu binary, tüm Janet çalışma zamanını, garbage collector'ı ve hatta bytecode derleyicisini içerir; dolayısıyla çalışma zamanında Janet kodu değerlendiren programlar da yapılabilir
- Janet çalışma zamanı küçük bir C kütüphanesidir; bağlandıktan sonra normal C fonksiyonları çağrılarak Janet değerleri işlenebilir
- Web sitelerine de gömülebilir ve Toodle gibi kullanıcı tanımlı programlanabilir DSL'lere sahip statik siteler oluşturulabilir
Metin ayrıştırma ve alt süreç DSL'i
- Janet'in metin işleme yaklaşımı regex yerine parsing expression grammar temellidir
- Parsing expression grammar, regex'ten daha basit, daha güçlü ve daha öngörülebilirdir; satır temelli değildir, bu yüzden çok satırlı metinleri ayrıştırabilir
- HTML, JSON ve diğer düzenli olmayan dilleri ayrıştırabilir; ayrıca rastgele null byte içeren ikili dosya biçimlerini de işleyebilir
- sh, pipe ve redirection'ı doğrudan Janet kodu içinde ifade etmeyi sağlayan üçüncü taraf bir shell scripting DSL'idir
($ find . -name *.janet | say)
- Bu DSL, Janet'i Perl'e makul bir alternatif olmaktan çıkarıp oldukça geniş bir program aralığı için Bash'e de makul bir alternatif haline getirir
Koleksiyonlar ve sözdizimi hissi
- Janet koleksiyon türleri hem değiştirilebilir hem değiştirilemez biçimlere sahiptir
- Değiştirilemez koleksiyonlar değer anlambilimine sahiptir; bu yüzden değiştirilemez vektör
[1 2], bellek adresi farklı olsa bile (take 2 [1 2 3]) ifadesinden ayırt edilmez
- Değiştirilebilir koleksiyonlar referans anlambilimine sahiptir; bu yüzden hash table
@{:x 1 :y 2} yalnızca kendisine eşittir ve aynı anahtarlarla aynı değerlere sahip başka bir hash table ayrı bir nesnedir
- Sözdizimi yaygın biçimde parantez kullanır ama listeler için
[], tablolar için {} kullanarak biçimleri ayırır
- Değiştirilebilir literal'ler her zaman
@"mutable string" örneğinde olduğu gibi @ öneki alır
- Anonim fonksiyonlar
(fn [x] (+ 1 x)) şeklinde yazılır; ayrıca |(+ 1 $) gibi bir ifadeyi fonksiyona yükselten | kısayolu da vardır
- Splat veya spread,
(+ ;args) örneğinde olduğu gibi ; ile ifade edilir
- Backtick string'ler istenen sayıda backtick ile açılıp aynı sayıda backtick ile kapatılabilir; backtick string içinde
\n gibi escape sequence'ler uygulanmaz
- Kalan parametreler
. yerine & kullanılarak (defn foo [first & rest] ...) şeklinde yazılır
- Janet reader macro desteklemez; bu yüzden sözdiziminin kendisi sabittir ve Janet okuyabilen biri tüm Janet programlarını okuyabilir
Makrolar ve derleme zamanı durumu
- Janet makroları, kod yazan koddur; derleme zamanında değerleri ve abstract syntax tree'leri işleyerek hem mevcut yürütme akışıyla hem de gelecekte çalışacak uygulama kodu akışıyla aynı anda uğraşır
- Janet makroları hijyenik değildir ve fonksiyonlar için ayrı bir ad alanı da yoktur
- Ancak literal fonksiyonlar unquote edilebildiği için tamamen referanssal olarak saydam makrolar yazılabilir
- Janet programı derlenirken önce üst düzey komutlar, normal ifadeler ve fonksiyon bildirimleri çalıştırılır, ardından program durumunun bir anlık görüntüsü diske yazılır
- Bu anlık görüntü paylaşılan referansları korur; bu yüzden yeniden başlatıldıktan sonra da değiştirilebilir değerler değiştirilmeye devam edilebilir
- Generator'lar bir sonraki devam ettirmede hangi komutun çalışacağını hatırlar ve closure'lar da kapattıkları değerleri korur
- Makrolar, derleme zamanında kod yürütmenin özel bir biçimidir ama bu yetenek makro olmadan da kullanılabilir
- Oyunlarda spline'lar önceden işlenebilir, dosyalar derleme zamanında okunup varlıklar nihai binary'nin içine yerleştirilebilir ve keyfi yan etkiler de gerçekleştirilebilir
- Janet for Mortals, SQL şema dosyalarına dayanarak veritabanı binding'lerini otomatik üreten bir örnek gösterir ve bunun çoğu dilde oldukça zor bir iş olduğunu söyler
Lisp geleneğine göre daha rahat
- Janet, eski Lisp geleneklerini olduğu gibi izlemez
CAR yerine first, PROGN yerine do, LAMBDA yerine fn, SETQ yerine def adları kullanılır
nil, boş liste değil bağımsız bir türdür ve boolean'lar birinci sınıf değerlerdir
EQ, EQL, EQUAL, EQUALP ailesinden kaçınır ve bağlı listeler de neredeyse hiç görünmez
2 yorum
Hacker News yorumları
Janet’in bazı eksikleri var. Başlıca olarak paket yönetiminde sürüm belirtme yetersiz ve gelişmiş HTTP routing gibi kütüphaneler genel olarak eksik
Yine de JPM ile binary ve script üretilebilmesi ve taşınabilirliğinin iyi olması gerçekten hoşuma gidiyor. Geçmişte kavram kanıtı olarak Playdate oyun konsoluna Janet programlama dilini çalıştırmayı bile denemiştim
Janet ile kod yazmayı seviyorum ama insanlar her seferinde bu dili benim yazdığımı sanınca biraz garip oluyor
Bunun bir de “Janet Janet yazar” versiyonu olsa güzel olurdu
Bağımlılıkları vendor ederek jpm olmadan da modern Janet bundle’larının kolayca kurulmasını sağlıyor
LLM geliştirmeye açıksan wrapper’ı LLM’e yazdırıp asıl mantığı Janet ile yazabilirsin
Aynı geliştiricinin daha önce yaptığı benzer bir dil olarak Fennel de var. Lua’ya derleniyor ve implementasyonunun tamamı da Lua ile yazılmış
Kendi standart kütüphanesi olmadığı için Janet’in parser kütüphanesi gibi pek çok güzel şey eksik, ama Lua gömülü ortamlarda script yazmak için iyi
https://fennel-lang.org/
Fennel ile Lua VM arasındaki bağ çok kırılgan ve kalite olarak Janet debugger’ı ve REPL’inin yarısına bile yaklaşmıyor. Fennel çok daha taşınabilir ve LuaJIT sayesinde SBCL’yi ezip geçebilir; bu yüzden insanın daha da zoruna gidiyor
Ama bence transpile deneyimi her şeyi baltalıyor. Geçici çözümler var ama
debug.setinfouygulanmış olsa bilematchblokları gibi keyifsiz sınır durumlarıyla karşılaşıyorsunLuaJIT2’yi fork’layıp debugging ve hata yapısını dil şeffaflığına daha uygun hale getirmekte büyük değer görüyorum. O zaman Fennel gibi diller çok daha cazip görünürdü
Yazının yazarı, daha önce HN’de de yer alan bu araçları Janet ile yaptı
https://bauble.studio
https://toodle.studio
Bu iki ilginç sanat aracı sayesinde bir süre Janet için oldukça heyecanlanmıştım
Janet’in ilgi görmesi beni hep sevindiriyor. Modern özelliklerinden biri olarak sandbox’ı özellikle anmak isterim
“Yorumlayıcının belirli sistem kaynaklarını kullanmasını engellemek için özellik kümeleri devre dışı bırakılır. Bir kez devre dışı bırakılan özellikleri yeniden etkinleştirmenin bir yolu yoktur.”
https://janet-lang.org/api/misc.html#sandbox
“SETQ is def” ifadesini görünce önce yüksek sesle “ne?” dedim. Çünkü SETQ binding oluşturmaz, sadece günceller
Belgeleri(https://janet-lang.org/docs/bindings.html) okuyunca yazarın gerçekten hatalı olduğunu gördüm; orada “def ile oluşturulan binding’ler değiştirilemez” deniyor. Muhtemelen “SETQ is set” demek istemişti
Janet, Guile, Tcl ve CL arasında tam yerinde duran bir şey gibi görünüyor, o yüzden gerçekten sevmek istiyorum; ama lambda ve kontrol akışı operatörlerinde köşeli parantez vektörleri kullanılmasına karşı içgüdüsel bir itirazım var. Clojure’da da aynı şekilde bunu aşmak benim için çok zor, ama yeterince çabalarsam belki alışabilirim
Bir de şu an LSP/SLIME durumunun nasıl olduğunu merak ediyorum. Bugünlerde epey önemli
Yuvarlak parantez kullanıldığında, listenin ilk öğesi geri kalan listenin nasıl yorumlanacağını belirler. Örneğin
(func a b c)bir fonksiyon çağrısıdır,(macro x y z)bir makro genişletmesidir,([p q r] …)ise parametre vektörüyle başlayan ve ardından çalıştırma ifadeleri gelen “çıplak” bir fonksiyon gövdesidirKöşeli parantezler, öğelerin aynı “türde” olduğu ve ilk öğenin özel olmadığı durumlarda kullanılır. Örneğin
(defn f [a b c] …)aynı türden parametrelerin bir kümesidir ve ilk parametre özel değildir;(let [a 1 b 2] …)de binding’lerin bir kümesidir ve ilk binding özel değildirAklıma gelen tek istisna,
caseiçinde birden fazla eşleşme öğesini gruplamak; bu da kullanım kolaylığı içindir. Bu mantığı anladıktan sonra fikrim değişti ve sonrasında bunu güzel bulmaya başladım[1 2 3]yerine(array 1 2 3)yazabilirsin,(fn [x] (+ 1 x))yerine de(f (x) (+ 1 x))yazabilirsinZorunlu değiller
Belirli bir uzunluğu aşan sistem script’lerinde Janet benim için sh, Python, awk vb. araçların yerini aldı
Script başlatma süresi çok hızlı; benim sistemimde hyperfine’a göre 1.4ms, yani dash’in 1ms değerine yakın. Bu derlenmiş çalıştırılabilir dosya değil, script bazında
sh-dslmodülü sayesinde($ cmda w x | cmdb y z)gibi shell komutları çok zarif biçimde yazılabiliyor. Debugging için image yükleyebilme özelliği de büyük yardım sağlıyorÇok yakın zamanda kullanmaya başladım ama şimdiden en sevdiğim dillerden biri olacak gibi görünüyor; daha önce kullandığım diğer tek Lisp, SICP için MIT Scheme’di
Bu yazı ferahlatıcı. İnternetteki AI öncesi tartışmaların kokusu var
Yeni bir dil, yeni bir sözdizimi ve yıllardır kod yazan insanların hararetli tartışmaları var. Keşke birisi AI’a izin verilmeyen bir çevrimiçi topluluk başlatsa
Aslında bir önceki yeniden lansman demek daha doğru olabilir; sanırım şu anda yine yeni bir ana sayfa var. Çevrimiçi topluluklarda AI’ı güvenilir biçimde engellemenin yolunu ilk bulan kişi muhtemelen çok zengin olur
https://www.techspot.com/news/111698-digg-relaunch-fails-two...
Bir tür “insanlık kanıtı” çözmesi zor bir problem
Yönetimin koyduğu tam kural “anlamlı insan yazarlığı” idi ama buna aldanmayın. lobsters’ta LLM’lere ideolojik olarak karşı olan çok kişi var. Teknolojinin ne kadar “anlamlı” kullanıldığı pek önemli değil
Benim çalışmam sadece AI değdi diye çöp diye sınıflandırıldı ve AI kullandığımı söylediğimde bana teşhirci ya da fetişist diyenler bile oldu. Katılmayı düşünenleri önceden uyarmak isterim
“Literal function’ları unquote edebilmek Janet’in tamamen referanssal olarak şeffaf macro’lar yazmasına izin veriyor” gibi cümlelerde Lispçilerin gerçekten çok soyut şeylere heyecanlandığı görülüyor
Sokaktaki sıradan birine bunu söyleseniz muhtemelen kaçmak ister
#define MULTIPLY(x, y) x * yint result = MULTIPLY(2 + 3, 4); // 14Bir şeyin ne anlama geldiğini bilmemek, onun kötü olduğu anlamına gelmez. İfadeye bakınca sanırım kastedilen de buydu
Programlamada tekrar tekrar ortaya çıkan kalıp ve sorunlar için ortak bir dilimizin olması iyi bir şey. “Şu programcı grubu ne kadar tuhaf” diye terimleri küçümsemek anlamsız ve ters teper
Yeniden başlamayı düşünüyorum ama niş özelliklerin uygulamaya değip değmediğini sorguluyorum. Bunları gerçekleştirmek benim için de zor.
dynamic-unwind’ı atlayıp, belkicall/cc’yi de çıkarıp onun yerine debug edilebilirlik, ekosistem, performans ve package management’e odaklanmak daha iyi olabilirBu yüzden ya çok muğlak şekilde “bilgisayarlarla ilgili bir iş yapıyorum” diyorum ya da “pek ilginç bir iş değil” deyip konuyu değiştiriyorum. Biraz daha ayrıntıya girince insanlar çıkış yolu aramaya başlıyor
Dürüst olmak gerekirse, Lisp topluluklarının küçük kalmasının bu açıdan bir faydası olduğunu düşünüyorum. Örneğin çok eski Design Patterns bile kalıtım yerine composition tercih edilmesini öğütlüyordu ama nesne yönelimli programcılar yine de 15 katman derinliğinde hiyerarşiler kuruyordu
Janet’le ilk tanıştığımda şu belgeler gerçekten yardımcı olmuştu
https://janetdocs.org/tutorials
https://janet.guide/ bunu yazının yazarı hazırlamış
HN’de ara sıra çıkan Janet yazılarına ilgi duydum ama herkesin övdüğü Janet for Mortals bana hiç de ölümlüler için bir kitap gibi gelmedi
Diğer dillerle kıyaslayınca Janet gerçekten öğrenmesi kolay bir dil, o yüzden o kitabın zor gelmesi şaşırtıcı. Kitabı okumadım ama dile epey aşinayım ve açıkçası sadece övebilirim
Janet, Lisp 2.0 gibi göründüğü için sözdizimi de Lisp usulü
Lobste.rs görüşleri
Janet’e başladıktan sadece 10 ay sonra, APL ailesi dilleri dışında neredeyse her şeyi unutacak kadar ona kapıldım; topluluk dokümantasyon sitesini işletiyor ve öğreticiler de yazıyorum
İlk 3 hafta içinde tüm kişisel script’lerimi baştan yazdım, yeni geliştirdiğim operasyon yazılımlarını da Janet ile yazıyorum
Janet’in implementasyonunda dilin neredeyse tamamı bir hash map gibi çalışıyor; bu yüzden
(keys (curenv))ile yerel sembolleri,(keys (getproto (curenv)))ile çekirdek sembolleri görebiliyorsunuz. İsterseniz hash map tabanlı, CLOS benzeri bir şey de kurabiliyorsunuz; bunun için bir implementasyon da varJoy web framework ile yaklaşık 20 web sitesini ve çeşitli servisleri 512 MB’lık tek bir ücretsiz VPS üzerinde çalıştırıyorum; bununla ilgili bir öğretici de yazdım
Yine de “değişmez koleksiyonlar” ifadesi gerçeği tam yansıtmıyor; standart kütüphane çoğunlukla değiştirilebilir değerler döndürüyor, bu yüzden şu an değişmezlikte ısrar etmek için çok güçlü bir gerekçe yok
Derleme zamanındaki değerleri çalışma zamanına aktarma özelliği özellikle güçlüydü. Örneğin İncil
.tsvdosyasını derleme sırasında bir hash map olarak ikili dosyanın içine gömerseniz, çalışma anında sadece sorgulama yapmanız gerekir; hattaembedkullanan Go sürümünden iki kat hızlı sonuç da aldımAynı şeyi Go ile doğrudan hash map biçiminde uygularsanız çok daha uzun sürüyor, ama Janet ile bir Lisp to Go derleyicisini bile 46 satırda yazabildim
Ian Henry’nin belirttiği daha ilginç nokta ise Janet’in closure durumunu imajlar/oturumlar arasında koruması; ilgili ortamı
(curenv)hash map’ine kaydedip yeni bir REPL oturumunda geri yükleseniz bile closure iç durumu devam ediyorLisp tabanlı bir müzik DSL’i olan https://lisp.trane.studio/ da var; makale https://dl.acm.org/doi/abs/10.1145/3677996.3678285 ve sonuç örnekleri https://x.com/greg_ash/status/1824218993118388708 da görülmeye değer
Kendi yazdığım bir kütüphane de var; farklı veri yapıları üzerinde SQL benzeri bir sorgu sözdizimi sunuyor
DataFrame ekleme·güncelleme ile CSV kaydetme/yüklemeyi destekliyor; ayrıca Datalog ve miniKanren içeriyor, APL benzeri vektörleştirilmiş işlemler de yapabiliyor
Janet içinde J’yi doğrudan kullanan jnj de var; Joy Web Framework’te
(var account (db/find-by :account :where {:login (auth-result :login)}))gibi bir veritabanı sorgu DSL’i bulunuyor ve bu, gerçek bir web sitesinin kimlik doğrulama kodunda da kullanılıyor“Değişmez koleksiyonlar” denince akla kalıcı veri yapıları geliyor; bunlar faydalı olsa da Janet’in temel özelliği değiller
Asıl hoşuma giden şey değer tipleri ile referans tipleri arasındaki simetri; yazının sonunda “immutable composite values” denmişti ama bunu ben yazmamış olsaydım, ben de ne demek istendiğini hemen anlamakta zorlanırdım
Janet taze ve gömülebilir bir dil, bu yüzden oyun motoru gibi projelerde yerleşik script dili olarak denemek isterim
DLL değiştirmeden hızlı yineleme için hot reload gereken yerlere çok uygun görünüyor; Lua da harika ama Janet bazı açılardan daha ifade gücü yüksek görünüyor
Janet bir dil olarak gerçekten harika; bir gün bir Zig projesinde script dili olarak kullanmak isterim. Daha fazla kişinin Janet’ten bahsetmesini görmek sevindirici
İyi görünüyor ama ben zaten babashka ile Clojure scripting’e alışıyorum, o yüzden benzer hissettiriyor. Gömülebilirlik dışında kaçırdığım büyük bir avantaj olup olmadığını merak ediyorum
“Kalan argümanlı dizi destructuring potansiyel olarak pahalı kopyalamalara yol açıyor” gibi kısımlar genel olarak daha az fonksiyonelmiş gibi görünüyor ve bu hoşuma gitmiyor
Başka dillerde metin parse ederken her seferinde eksikliğini hissediyorum
Janet, küçük ve gömülebilir bir Clojure’dan ziyade, fonksiyonel programlama desteği daha iyi olan bir Lua’ya daha yakın