- Janet adalah dialek Lisp kecil, tetapi memiliki bahasa imperatif, fungsi kelas satu, namespace pengenal tunggal, dan lexical block scope sehingga mudah untuk mulai secara sederhana
- Bahasa inti hanya terdiri dari 8 perintah:
do, def, var, set, if, while, break, fn, dan macro membuat pembungkus control flow yang lebih kuat atau lebih praktis
- Kemudahan distribusi dicapai dengan mengompilasi program Janet menjadi executable native yang ditautkan secara statis dengan runtime Janet, sehingga pengguna tidak perlu memasang Janet atau dependensi apa pun
- Parsing Expression Grammar (PEG) lebih sederhana, lebih kuat, dan lebih mudah diprediksi daripada regular expression, sementara sh memungkinkan pipe dan redirection diekspresikan di dalam Janet sehingga memperluas cakupan penulisan CLI
- Eksekusi saat waktu kompilasi menjalankan perintah tingkat teratas terlebih dahulu lalu menyimpan snapshot status program ke disk, sehingga nilai, referensi bersama, generator, dan status closure dapat diteruskan ke runtime bahkan tanpa macro
Inti yang sederhana
- Janet adalah bahasa imperatif dengan fungsi kelas satu, namespace pengenal tunggal, dan lexical block scope
- Inti bahasanya dijaga tetap kecil dengan 8 perintah:
do, def, var, set, if, while, break, fn
- Macro memungkinkan pembungkus control flow tingkat tinggi yang lebih kuat atau lebih praktis
- Semantik runtime-nya terasa familier, dan bagian bahasa lainnya juga kecil sampai seluruh standard library muat dalam satu halaman
Distribusi native dan embedding
- Program Janet dapat dengan mudah dikompilasi menjadi executable native yang ditautkan secara statis dengan runtime Janet
- Pengguna yang menerima distribusinya tidak perlu memasang Janet, dependensi proyek, atau komponen tambahan lainnya
- Janet mengompilasi dirinya menjadi bytecode, lalu menuliskan bytecode tersebut ke dalam file
.c yang memulai runtime Janet, kemudian mengompilasi file C itu dengan compiler C sistem
- Biner native “hello world” yang sederhana berukuran kurang dari 1MB, dan pada Janet 1.27.0 untuk aarch64 macOS ukurannya adalah 784K
- Biner ini juga memuat seluruh runtime Janet, garbage collector, hingga compiler bytecode, sehingga program yang mengevaluasi kode Janet saat runtime pun bisa dibuat
- Runtime Janet adalah library C kecil, sehingga setelah ditautkan Anda dapat memanipulasi nilai Janet dengan memanggil fungsi C biasa
- Ia juga dapat di-embed ke situs web, dan dapat digunakan untuk membuat situs statis dengan DSL terprogram kustom seperti Toodle
Parsing teks dan DSL subprocess
- Pemrosesan teks di Janet berbasis Parsing Expression Grammar, bukan regular expression
- Parsing Expression Grammar lebih sederhana, lebih kuat, dan lebih mudah diprediksi daripada regular expression, serta tidak terikat pada baris sehingga dapat mem-parsing teks multi-baris
- Ia dapat mem-parsing HTML, JSON, dan bahasa non-reguler lainnya, serta menangani format berkas biner yang mengandung null byte arbitrer
- sh adalah DSL scripting shell pihak ketiga yang memungkinkan pipe dan redirection diekspresikan langsung di dalam kode Janet
($ find . -name *.janet | say)
- DSL ini mengangkat Janet dari alternatif yang masuk akal untuk Perl menjadi alternatif yang masuk akal untuk Bash pada rentang program yang cukup luas
Koleksi dan nuansa sintaks
- Tipe koleksi Janet memiliki bentuk mutable dan immutable
- Koleksi immutable memiliki value semantics, sehingga vektor immutable
[1 2] tidak dapat dibedakan dari (take 2 [1 2 3]) meskipun alamat memorinya berbeda
- Koleksi mutable memiliki reference semantics, sehingga hash table
@{:x 1 :y 2} hanya sama dengan dirinya sendiri, dan hash table lain dengan key dan value yang sama tetap merupakan objek terpisah
- Sintaksnya banyak memakai tanda kurung, tetapi membedakan bentuk dengan menggunakan
[] untuk list dan {} untuk table
- Literal mutable selalu memakai prefiks
@, seperti @"mutable string"
- Fungsi anonim ditulis sebagai
(fn [x] (+ 1 x)), dan juga tersedia notasi singkat yang mengangkat ekspresi menjadi fungsi dengan |, seperti |(+ 1 $)
- Splat atau spread menggunakan
;, seperti pada (+ ;args)
- String backtick dapat dibuka dengan sejumlah backtick sesuai keinginan dan ditutup dengan jumlah yang sama; di dalam string backtick, escape sequence seperti
\n tidak diterapkan
- Parameter sisa memakai
& alih-alih ., sehingga ditulis seperti (defn foo [first & rest] ...)
- Janet tidak mendukung reader macro, sehingga sintaksnya sendiri tetap tetap, dan jika Anda bisa membaca Janet maka Anda bisa membaca semua program Janet
Macro dan status saat waktu kompilasi
- Macro Janet adalah kode yang menulis kode, dan pada waktu kompilasi ia menangani sekaligus alur eksekusi saat ini yang memanipulasi nilai dan abstract syntax tree, serta alur kode aplikasi yang akan dijalankan di masa depan
- Macro Janet tidak hygienic, dan juga tidak memiliki namespace terpisah untuk fungsi
- Namun, fungsi literal dapat di-unquote sehingga macro yang sepenuhnya referentially transparent tetap dapat ditulis
- Saat program Janet dikompilasi, perintah tingkat teratas, pernyataan biasa, dan deklarasi fungsi dijalankan terlebih dahulu, lalu snapshot status program ditulis ke disk
- Snapshot ini mempertahankan referensi bersama, sehingga setelah dijalankan kembali, nilai mutable masih bisa terus diubah
- Generator mengingat perintah yang harus dijalankan saat dilanjutkan berikutnya, dan closure juga mempertahankan nilai yang telah ditutupinya
- Macro adalah bentuk khusus dari eksekusi kode saat waktu kompilasi, tetapi kemampuan ini juga dapat digunakan tanpa macro
- Dalam game, spline dapat diproses lebih dahulu, berkas dapat dibaca saat waktu kompilasi untuk memasukkan aset ke dalam biner akhir, dan efek samping arbitrer pun bisa dijalankan
- Janet for Mortals menunjukkan contoh pembuatan binding database otomatis berdasarkan berkas skema SQL, dan menilai bahwa pekerjaan seperti ini cukup sulit di kebanyakan bahasa
Lebih nyaman daripada tradisi Lisp
- Janet tidak begitu saja mengikuti konvensi Lisp lama
CAR diberi nama first, PROGN menjadi do, LAMBDA menjadi fn, dan SETQ menjadi def
nil bukan list kosong melainkan tipe tersendiri, dan boolean adalah nilai kelas satu
- Ia menghindari keluarga
EQ, EQL, EQUAL, EQUALP, dan linked list pun hampir tidak terlihat
2 komentar
Komentar Hacker News
Janet punya beberapa kekurangan. Terutama penentuan versi manajemen paket yang masih kurang, dan juga ekosistem library yang belum lengkap untuk hal-hal seperti routing HTTP tingkat lanjut.
Meski begitu, saya sangat suka bahwa dengan JPM kita bisa membuat biner dan skrip, dan portabilitasnya juga sangat bagus. Dulu saya bahkan pernah mencoba menjalankan bahasa pemrograman Janet di konsol game Playdate sebagai proof of concept.
Saya senang menulis kode dengan Janet, tapi setiap kali begitu, agak merepotkan karena orang-orang mengira saya yang membuat bahasa ini
Akan seru kalau ada versi “Janet menulis Janet” juga
Tool ini melakukan vendoring dependensi dan memudahkan pemasangan bundle Janet modern tanpa jpm
Jika terbuka pada pengembangan dengan LLM, Anda bisa membiarkan wrapper ditulis oleh LLM dan menulis logika sebenarnya dalam Janet
Ada juga bahasa serupa yang dibuat lebih awal oleh pengembang yang sama, yaitu Fennel. Bahasa ini dikompilasi ke Lua dan implementasinya juga seluruhnya ditulis dalam Lua.
Karena tidak punya standard library sendiri, banyak hal bagus seperti library parser Janet yang tidak ada, tetapi tetap bagus untuk menulis skrip di lingkungan yang menanamkan Lua.
https://fennel-lang.org/
Keterhubungan antara Fennel dan Lua VM sangat rapuh, dan kualitasnya bahkan tidak sampai setengah dari debugger dan REPL Janet. Sayang sekali, karena Fennel jauh lebih portabel dan dengan LuaJIT bahkan bisa mengungguli SBCL.
Tapi pengalaman transpile itu sendiri menurut saya benar-benar menjadi hambatan besar. Ada beberapa jalan memutar, tetapi bahkan jika
debug.setinfodiimplementasikan, Anda tetap akan menemui kasus batas yang kurang menyenangkan seperti blokmatch.Saya rasa ada nilai besar dalam mem-fork LuaJIT2 untuk memperbaiki struktur debugging dan error agar lebih sesuai dengan transparansi bahasa. Kalau itu dilakukan, bahasa seperti Fennel akan terlihat jauh lebih menarik
Penulis artikel ini dulu juga membuat tool-tool ini dengan Janet, yang pernah dibahas di HN
https://bauble.studio
https://toodle.studio
Dua tool seni yang menarik ini sempat membuat saya cukup berharap pada Janet untuk beberapa waktu
Saya selalu senang melihat Janet mendapat perhatian. Salah satu fitur modern yang ingin saya soroti adalah sandbox
“Menonaktifkan kumpulan kemampuan agar interpreter tidak dapat menggunakan resource sistem tertentu. Kemampuan yang sudah dinonaktifkan tidak bisa diaktifkan kembali.”
https://janet-lang.org/api/misc.html#sandbox
Saat melihat “SETQ is def”, reaksi pertama saya adalah berkata keras-keras, “Apa?” Itu karena SETQ tidak membuat binding, melainkan hanya memperbaruinya.
Setelah membaca dokumentasinya (https://janet-lang.org/docs/bindings.html), ternyata penulis memang salah, dan di sana tertulis bahwa “binding yang dibuat dengan def bersifat immutable”. Mungkin yang dimaksud sebenarnya adalah “SETQ is set”.
Saya benar-benar ingin menyukai Janet, karena terlihat seperti titik yang pas di antara Guile, Tcl, dan CL, tetapi saya punya penolakan naluriah terhadap penggunaan vektor kurung siku untuk lambda dan operator alur kontrol. Saya juga merasakan hal yang sama pada Clojure sehingga sulit sekali melewatinya, tetapi mungkin dengan cukup usaha saya bisa terbiasa.
Dan saya juga penasaran bagaimana keadaan LSP/SLIME saat ini. Sekarang itu cukup penting
Jika memakai kurung biasa, elemen pertama dari daftar menentukan bagaimana sisa daftar ditafsirkan. Misalnya,
(func a b c)berarti pemanggilan fungsi,(macro x y z)berarti ekspansi makro, dan([p q r] …)adalah tubuh fungsi “telanjang” yang dimulai dengan vektor parameter lalu diikuti ekspresi-eksekusi.Kurung siku dipakai ketika elemen-elemennya memiliki “jenis” yang sama dan elemen pertama tidak istimewa. Misalnya,
(defn f [a b c] …)adalah kumpulan parameter yang sejenis dan parameter pertama tidak punya peran khusus, dan(let [a 1 b 2] …)juga merupakan kumpulan binding di mana binding pertama tidak istimewa.Satu-satunya pengecualian yang terpikir adalah saat
casemengelompokkan beberapa elemen pencocokan, tetapi itu demi kenyamanan. Setelah saya memahami logika ini, pandangan saya berubah, dan sejak itu saya justru merasa sintaks ini indah[1 2 3], Anda bisa menulis(array 1 2 3), dan alih-alih(fn [x] (+ 1 x)), Anda bisa menulis(f (x) (+ 1 x))Itu tidak wajib
Untuk skrip sistem yang melebihi panjang tertentu, Janet bagiku menggantikan sh, Python, awk, dan lainnya
Waktu startup eksekusi skripnya sangat cepat, dan di sistemku menurut hyperfine sekitar 1,4 ms, mirip dengan 1 ms milik dash. Ini berdasarkan skrip, bukan executable yang dikompilasi
Berkat modul
sh-dsl, perintah shell bisa ditulis dengan sangat elegan seperti($ cmda w x | cmdb y z). Fitur untuk memuat image demi debugging juga sangat membantuAku baru mulai memakainya belum lama ini, tapi rasanya ini sudah akan jadi salah satu bahasa favoritku, dan Lisp lain yang pernah kupakai sebelumnya cuma MIT Scheme untuk SICP
Tulisan ini terasa segar. Masih ada aroma diskusi pra-AI di internet
Ada bahasa baru, sintaks baru, dan perdebatan sengit dari orang-orang yang sudah bertahun-tahun menulis kode. Akan bagus kalau ada seseorang yang memulai komunitas online yang tidak mengizinkan AI
Sebenarnya mungkin lebih tepat menyebutnya peluncuran ulang sebelumnya, dan sekarang sepertinya sudah ada homepage baru lagi. Orang pertama yang menemukan cara andal untuk memblokir AI di komunitas online kemungkinan besar akan menjadi sangat kaya
https://www.techspot.com/news/111698-digg-relaunch-fails-two...
Semacam “bukti kemanusiaan” adalah masalah yang sulit dipecahkan
Aturan persis yang ditetapkan admin adalah “karya manusia yang bermakna”, tapi jangan terkecoh. Di lobsters banyak orang yang secara ideologis menentang LLM. Seberapa “bermakna” teknologi itu diterapkan tidak terlalu penting
Karyaku dikategorikan sebagai sampah hanya karena pernah tersentuh AI, dan ada juga orang yang menyebutku ekshibisionis atau punya fetish ketika aku bilang bahwa aku memakai AI. Rasanya ini perlu diberi tahu lebih dulu kepada orang yang mempertimbangkan untuk mendaftar
Kalimat seperti “dengan memungkinkan unquote pada fungsi literal, Janet bisa menulis macro yang sepenuhnya referentially transparent” membuat orang-orang Lisp tampak benar-benar bersemangat pada hal-hal yang sangat abstrak
Kalau kamu mengatakan itu kepada orang biasa di jalan, mereka mungkin akan langsung kabur
#define MULTIPLY(x, y) x * yint result = MULTIPLY(2 + 3, 4); // 14Hanya karena kamu tidak tahu arti suatu istilah bukan berarti istilah itu buruk. Dari ungkapannya, sepertinya memang itu maksud yang hendak disampaikan
Punya bahasa bersama untuk pola dan masalah yang berulang dalam pemrograman adalah hal yang baik. Mengejek istilah dengan nada “kelompok programmer itu aneh sekali” tidak ada gunanya dan malah kontraproduktif
Aku sedang berpikir untuk melanjutkannya lagi, tapi masih ragu apakah fitur-fitur niche layak diimplementasikan. Bagiku itu juga sulit dibuat. Mungkin lebih baik melewati
dynamic-unwind, mungkin juga membuangcall/cc, lalu fokus pada kemudahan debugging, ekosistem, performa, dan manajemen paketJadi aku bilang sangat samar seperti “saya kerja di bidang komputer”, atau “pekerjaannya tidak terlalu menarik”, lalu mencoba mengganti topik. Begitu sedikit lebih spesifik, orang-orang mulai mencari jalan keluar
Sejujurnya, menurutku komunitas keluarga Lisp justru mendapat manfaat karena ukurannya kecil. Misalnya, bahkan buku tua seperti Design Patterns sudah memperingatkan agar lebih memilih komposisi daripada pewarisan, tapi programmer berorientasi objek tetap saja membuat hierarki sedalam 15 tingkat
Saat pertama kali mengenal Janet, dokumen-dokumen ini sangat membantu
https://janetdocs.org/tutorials
https://janet.guide/ dibuat oleh penulisnya
Aku tertarik tiap kali ada tulisan tentang Janet yang sesekali muncul di HN, tapi Janet for Mortals yang sangat dipuji orang sama sekali tidak terasa seperti buku untuk manusia fana
Dibandingkan bahasa lain, Janet benar-benar termasuk yang mudah dipelajari, jadi mengejutkan kalau buku itu terasa sulit. Aku belum membaca bukunya, tapi cukup akrab dengan bahasanya, dan sejujurnya yang bisa kulakukan hanya memujinya
Janet terlihat seperti Lisp 2.0, jadi sintaksnya pun bergaya Lisp
Komentar Lobste.rs
Setelah 10 bulan memakai Janet, saya sampai nyaris melupakan semua bahasa non-keluarga APL karena begitu tenggelam di dalamnya, dan sambil mengelola situs dokumentasi komunitas, saya juga sedang menulis tutorial
Dalam 3 minggu pertama saya menulis ulang semua skrip pribadi saya, dan software operasional baru yang saya buat juga saya tulis dengan Janet
Secara implementasi, hampir seluruh bahasa Janet bekerja seperti hash map, jadi simbol lokal bisa dilihat dengan
(keys (curenv)), simbol inti dengan(keys (getproto (curenv))), dan kalau mau bahkan bisa membuat sesuatu yang mirip CLOS berbasis hash map; ada juga implementasinyaDengan framework web Joy, saya menjalankan sekitar 20 situs web dan beberapa layanan di satu VPS gratis 512MB, dan saya juga menulis tutorial terkait
Namun, istilah “koleksi immutable” agak kurang tepat dengan kenyataannya; pustaka standarnya umumnya mengembalikan nilai yang mutable, jadi saat ini tidak banyak alasan untuk ngotot mengejar immutability
Fitur yang meneruskan nilai waktu kompilasi ke waktu eksekusi terasa sangat kuat. Misalnya, jika
.tsvAlkitab dimasukkan ke biner sebagai hash map saat kompilasi, maka saat runtime tinggal dilakukan lookup saja, dan hasilnya bahkan dua kali lebih cepat dibanding versi Go yang memakaiembedJika hal yang sama diimplementasikan manual di Go dalam bentuk hash map, kodenya jadi jauh lebih panjang, tetapi dengan Janet saya bahkan bisa membuat kompiler Lisp ke Go hanya dalam 46 baris
Bagian yang menurut Ian Henry lebih menarik adalah bahwa Janet mempertahankan state closure antar image/sesi; jika lingkungan terkait disimpan di hash map
(curenv)lalu dipulihkan di sesi REPL baru, state internal closure tetap berlanjutAda juga DSL musik berbasis Lisp di https://lisp.trane.studio/, serta paper https://dl.acm.org/doi/abs/10.1145/3677996.3678285 dan contoh hasilnya di https://x.com/greg_ash/status/1824218993118388708 yang layak dilihat
Saya juga punya library buatan sendiri yang menyediakan sintaks query mirip SQL di atas berbagai struktur data
Library itu mendukung insert·update dataframe serta simpan/muat CSV, dan juga mencakup Datalog dan miniKanren, plus operasi tervektorisasi ala APL
Ada juga jnj yang memungkinkan memakai J langsung di Janet, dan Joy Web Framework punya DSL query DB seperti
(var account (db/find-by :account :where {:login (auth-result :login)})), yang juga dipakai di kode autentikasi situs web nyataKalau mendengar “koleksi immutable”, yang terbayang biasanya struktur data persisten, dan meskipun itu berguna, itu bukan fitur dasar Janet
Yang sebenarnya saya sukai adalah kesimetrian antara tipe nilai dan tipe referensi, dan meskipun di akhir tulisan tertulis “immutable composite values”, kalau itu bukan tulisan saya sendiri mungkin saya juga tidak akan langsung menangkap maksudnya
Janet terasa segar dan merupakan bahasa yang bisa di-embed, jadi saya ingin mencobanya untuk scripting bawaan di proyek seperti game engine
Bahasa ini tampak cocok untuk tempat yang membutuhkan hot reload demi iterasi cepat tanpa mengganti DLL; Lua juga hebat, tetapi dalam beberapa hal Janet terlihat lebih ekspresif
Janet benar-benar keren sebagai bahasa, dan suatu hari saya ingin memakainya sebagai bahasa skrip di proyek Zig. Senang melihat semakin banyak orang membicarakan Janet
Kelihatannya bagus, tetapi saya sudah terbiasa dengan scripting Clojure lewat babashka, jadi rasanya mirip. Saya penasaran apakah ada kelebihan besar lain yang saya lewatkan selain kemampuan embed
Bagian seperti “destructuring array dengan argumen sisa berpotensi menimbulkan copy yang mahal” membuatnya secara umum terasa kurang fungsional, dan saya kurang suka itu
Setiap kali saya mem-parsing teks di bahasa lain, saya jadi merasa kehilangan fitur itu
Daripada Clojure kecil yang bisa di-embed, Janet lebih mirip Lua dengan dukungan functional programming yang lebih baik