Düşündünüz mü hiç, parantezler bu modern çağda hala bir programlama dilinin temelini oluşturabilir mi? Son birkaç yıldır eğlenceli yan projelerde favori programlama dili haline gelen küçük bir Lisp diyalektiği olan Janet, bu soruyu yanıtlıyor. Janet’e olan hayranlığımız o kadar büyük ki, dili daha fazla kişiye tanıtmak amacıyla hakkında ücretsiz bir kitap bile yazdık. Bu makale, neden Janet’e bir şans vermeniz gerektiğini size ikna etmeye çalışacak.
Janet Basit Bir Dil
Janet, birinci sınıf fonksiyonlara, tanımlayıcılar için tek bir isim alanına ve leksik blok kapsamlamasına sahip imperatif bir dildir. Dilin çekirdeği son derece küçüktür; yalnızca sekiz talimattan oluşur: do, def, var, set, if, while, break, fn. Ancak makrolar sayesinde, daha güçlü veya kullanışlı kontrol akışı sağlayan birçok yüksek seviyeli sarmalayıcı mevcuttur.
Janet’i bir öğleden sonra ‘öğrenebilirsiniz’, çünkü çalışma zamanı semantiği oldukça tanıdıktır: JavaScript’i düşünün, artı değer tipleri, eksi tüm tuhaflıkları. Dilin geri kalanı da küçüktür; tüm standart kütüphane tek bir sayfaya sığar. Başlangıçtaki bu kolaylık, bizi Janet’e bağlayan ilk şey oldu.
Janet Dağıtılabilir Bir Yapıya Sahip
Janet programlarını, Janet çalışma zamanını statik olarak bağlayan yerel yürütülebilir dosyalara derlemek oldukça kolaydır. Bu programları başkalarıyla, önce Janet’i veya projenizin bağımlılıklarını kurmalarını istemeden paylaşabilirsiniz. Hatta onlara Janet ile yazıldığını söylemenize bile gerek yok!
Janet bunu çok zarif bir şekilde başarır: Janet kendini bayt koduna derler ve ardından bu bayt kodu, Janet çalışma zamanını da başlatan bir .c dosyasına yazar. Sonra bu C dosyası, sisteminizin C derleyicisi ile derlenir. Janet, kolayca gömülebilir olacak şekilde tasarlandığından, bu mantıklıdır: esasen kendini önemsiz bir C yürütülebilir dosyasına gömmektedir.
Basit bir Janet ‘merhaba dünya’ programı, yerel bir ikiliye derlendiğinde bir megabaytın altında yer kaplar (aarch64 macOS’ta Janet 1.27.0 için 784K, ancak sonuçlar farklılık gösterebilir). Bu, tam Janet çalışma zamanını, çöp toplayıcısını ve hatta bayt kodu derleyicisini içerir, böylece çalışma zamanında Janet kodu değerlendiren programlar yazabilirsiniz, isterseniz. Bu, Janet’i küçük komut satırı uygulamaları yazmak için mükemmel bir seçim yapar, özellikle de şunu düşündüğünüzde…
Janet Metin Ayrıştırmada Olağanüstü Derecede İyi
Normal ifadeler yerine, Janet’in metin işleme yeteneği _ayrıştırma ifade gramerlerine_ (parsing expression grammars) dayanır. Ayrıştırma ifade gramerleri, normal ifadelere göre daha basit, daha güçlü ve daha tahmin edilebilirdir. Satır odaklı olmadıkları için çok satırlı metni sorunsuz bir şekilde ayrıştırabilirler. Ayrıca HTML, JSON veya başka herhangi bir düzenli olmayan dili de ayrıştırabilirler. Hatta _ikili_ dosya biçimlerini bile ayrıştırabilirler; rastgele boş baytlarla sorun yaşamazlar.
Onlar gerçekten _ayrıştırıcılardır_: yapılandırılmış, birleştirilebilir, birinci sınıf ayrıştırıcılar. Ve öğrenmeleri oldukça kolaydır!
Janet, Yüksek Seviyeli Diller Arasında En İyi Alt Süreç DSL’sine Sahip
Janet’te boruları ve yönlendirmeleri doğrudan ifade etmenizi sağlayan bir kabuk betik DSL’si sunan sh adında üçüncü taraf bir kütüphane bulunmaktadır. Örneğin: ($ find . -name *.janet | say) Bu oldukça inanılmaz. O kadar güzel bir DSL ki, ona _Janet for Mortals_ kitabımızın tüm bir bölümünü adadık. Bu özellik, Janet’i Perl’e makul bir alternatiften, şaşırtıcı derecede geniş bir program yelpazesi için _Bash_’e makul bir alternatife dönüştürür.
Janet Gömülebilir Bir Yapıda
Lua, fiili ‘gömülü dil’ haline gelmiştir, ki bu biraz utanç verici, çünkü… neyse, bu Lua hakkında bir yazı değil. Bu özellik size çok önemli gelmeyebilir, ancak henüz denemediğiniz için olabilir: betik arayüzleri sunan programlar yazabilmek oldukça eğlenceli bir süper güçtür.
Janet’i gömmek çok kolaydır: Janet çalışma zamanı küçük bir C kütüphanesidir ve tek yapmanız gereken onu bağlamak ve ardından Janet değerlerini manipüle etmek için düzenli C fonksiyonlarını çağırmaktır. Hatta onu _web sitelerine_ bile gömebilir ve özel programlanabilir DSL’lerle statik siteler yazabilirsiniz!
Janet Değişken ve Sabit Koleksiyonlara Sahip
Janet’in koleksiyon tipleri değişken ve sabit (mutable ve immutable) çeşitlerde gelir. Sabit koleksiyonlar değer semantiğine sahiptir: [1 2] sabit vektörü, (take 2 [1 2 3]) ile aynıdır, farklı bellek adreslerine sahip olsalar bile. Değişken koleksiyonlar ise referans semantiğine sahiptir: @{:x 1 :y 2} karma tablosu sadece kendine eşittir. Aynı anahtar ve değerlere sahip başka bir karma tablo ayrı bir nesnedir. Her dilde standart kütüphaneye yerleşik sabit bileşik değerler bulunmaz!
Makrolar, Makrolar, Makrolar
Sanırım Janet’i öğrenmeniz için asıl neden budur, ancak sizi korkutmak istemediğim için başta bundan bahsetmek istemedim.
Makro yazmayı hiç öğrenmeden de Janet ile gayet iyi program yazabilirsiniz. Ama öğrenmelisiniz, çünkü makro yazmak _eğlencelidir_. Daha önce yaptığım hiçbir programlama türünden farklı hissettiriyor.
Makro yazmak aynı anda iki kez düşünmeyi gerektirir: kod yazan kod yazıyorsunuz, bu yüzden zihninizde iki yürütme iş parçacığını düz tutmalısınız: şimdi, derleme zamanında çalışan, değerleri ve soyut sözdizimi ağaçlarını manipüle eden kod ve manipüle ettiğiniz, ürettiğiniz uygulama kodu, yani gelecekte çalışacak kod.
Janet’in makroları hijyenik değildir ve Janet’in fonksiyonlar için ayrı bir isim alanı yoktur. Ancak literal fonksiyonları alıntıdan çıkarmanıza izin vererek, Janet tamamen referans şeffaf makrolar yazmayı mümkün kılar. Aksi takdirde çok hassas bir soruna inanılmaz derecede basit ve zarif bir çözümdür. Ve bunu Janet’te yapabilmenin mümkün olması, bir sonraki favori özelliğimi vurgulamaktadır…
Janet, Derleme Zamanından Çalışma Zamanına Değer Aktarmanızı Sağlar
Bu, bana göre Janet’in en ilginç özelliğidir. Ama ilk başta çok ilginç gelmeyebilir; aslında sadece herhangi bir Janet değerinin diske serileştirileceği ve daha sonra okunabileceği anlamına gelir.
Ancak bu serileştirme örtüktür: bir Janet programını derlediğinizde, tüm üst düzey talimatları (düzenli ifadeler, fonksiyon bildirimleri vb.) çalıştırır ve tüm üst düzey değerleri yürüttükten sonra, Janet programınızın durumunun bir anlık görüntüsünü diske yazar.
Ve bu, programınızın durumunun _tam_ bir anlık görüntüsüdür: paylaşılan referanslar korunur, böylece değişken değerler ‘anlık görüntüyü’ sürdürdükten sonra hala değiştirilebilir. Üreteçler, bir sonraki sefer sürdürdüğünüzde hangi talimatı çalıştırmaları gerektiğini tam olarak hatırlarlar. Kapatmalar kapanmaya devam eder. Makrolar, derleme zamanı kod yürütmesinin özel bir durumudur — yeni fonksiyonlar oluşturmak için soyut sözdizimi ağaçlarını manipüle etmek — ancak bu, hiç makro kullanmadan keyfini çıkarabileceğiniz bir süper güçtür. Bir oyun mu yapıyorsunuz? Splinlerinizi önceden düzenleyin! Veya derleme zamanında dosyaları okuyarak varlıkları nihai ikili dosyanıza gömün – isteğe bağlı yan etkiler gerçekleştirebilirsiniz!
_Janet for Mortals_ kitabında, bunu bir SQL şema dosyasına dayalı olarak veritabanı bağlamalarını otomatik olarak oluşturmak için kullanmaya dair bir örnek vardır (buradan bakabilirsiniz) – biraz saçma bir örnek, ancak çoğu dilde yapılması oldukça zor olacak bir şey.
Janet Ele İyi Geliyor
Bu tamamen özneldir, ancak Janet’in sözdizimini seviyorum. Basitlik, tekdüzelik ve çeşitliliğin mükemmel bir dengesini kurar.
- Yaygın parantezler kullanır, ancak listeler için
[]ve tablolar için{}ile ayrılır. - Değişken değişmezler her zaman
@ile ön eklenir:@"mutable string",{:immutable hash-table}, vb. - Anonim fonksiyonlar
(fn [x] (+ 1 x))şeklinde yazılır, ancak herhangi bir ifadeyi|ile fonksiyona dönüştürmek için kısa bir gösterim vardır:|(+ 1 $). - Janet,
;ile ‘splat’ veya ‘spread’ özelliğini destekler:(+ ;args). - Dize değişmezleri herhangi bir sayıda backtick ile yazılabilir ve aynı sayıda backtick ile kapatılabilir.
\ngibi kaçış dizileri backtick tırnaklı dizelerde geçerli değildir, böylece hiçbir zaman bunları nasıl kaçıracağınızı düşünmeden herhangi bir içerikli dizeler oluşturabilirsiniz – tek yapmanız gereken yeterli sayıda backtick içine almaktır. - Geri kalan parametreler
.yerine&kullanır:(defn foo [first & rest] ...). - Janet okuyucu makroları desteklemez, bu nedenle sözdizimi sabittir. Janet’i nasıl okuyacağınızı biliyorsanız, tüm Janet programlarını okuyabilirsiniz. Bu, onları anlayabileceğiniz anlamına gelmez…
Janet Gelenekten Çok Rahatlığı Tercih Eder
Janet eski geleneklere bağlı kalmaz. CAR, first olarak adlandırılır. PROGN, do olarak adlandırılır. LAMBDA, fn‘dir ve SETQ, def‘tir. nil boş liste değildir; kendi tipidir ve dilde birinci sınıf Boole değerleri vardır. EQ, EQL, EQUAL ve EQUALP‘den kaçınır. Hiçbir bağlı liste görülmez.
Bu gerçekten _iyi_ ya da _kötü_ değildir, ancak belirtmeye değer olduğunu düşündüm: parantezleri görüp FORMAT‘ın da çok uzakta olmadığını varsaydıysanız, belki Janet’e bir kez daha bakın.

