- Janet एक छोटी Lisp dialect है, लेकिन इसमें imperative language features, first-class functions, single identifier namespace, और lexical block scope हैं, इसलिए इसे सरलता से शुरू किया जा सकता है
- core language सिर्फ 8 commands —
do, def, var, set, if, while, break, fn — से बनी है, और macros ज़्यादा शक्तिशाली या सुविधाजनक control-flow wrappers बना देते हैं
- deployment इस तरह संभव है कि Janet program को Janet runtime के साथ statically link किए गए native executable के रूप में compile किया जाए, जिससे user को Janet या dependencies install नहीं करनी पड़तीं
- Parsing Expression Grammar (PEG) regular expressions से अधिक सरल, शक्तिशाली और पूर्वानुमेय है, और sh Janet के भीतर pipes और redirection को व्यक्त करने देता है, जिससे CLI लिखने का दायरा बढ़ता है
- compile-time execution top-level commands को पहले चलाकर program state का snapshot disk पर सेव करता है, जिससे macros के बिना भी values, shared references, generators और closure state को runtime तक पहुँचाया जा सकता है
सरल core
- Janet एक imperative language है और इसमें first-class functions, single identifier namespace, और lexical block scope हैं
- language core को
do, def, var, set, if, while, break, fn इन 8 commands तक छोटा रखा गया है
- macros अधिक शक्तिशाली या सुविधाजनक high-level control-flow wrappers संभव बनाते हैं
- runtime semantics परिचित हैं, और standard library पूरी की पूरी एक पेज में आ जाती है, इसलिए language का बाकी हिस्सा भी छोटा है
Native deployment और embedding
- Janet programs को Janet runtime के साथ statically linked native executables में आसानी से compile किया जा सकता है
- जिसे यह deploy किया जाए, उस user को Janet, project dependencies, या किसी और अलग component को install करने की ज़रूरत नहीं होती
- Janet पहले खुद को bytecode में compile करता है, फिर उस bytecode को Janet runtime शुरू करने वाली
.c file में लिखता है, और system C compiler से उस C file को compile करता है
- एक साधारण “hello world” native binary 1MB से छोटी होती है, और Janet 1.27.0 के aarch64 macOS पर इसका आकार 784K था
- इस binary में पूरा Janet runtime, garbage collector, और bytecode compiler तक शामिल होता है, इसलिए ऐसे programs भी बनाए जा सकते हैं जो runtime पर Janet code evaluate करें
- Janet runtime एक छोटी C library है, इसलिए इसे link करने के बाद सामान्य C functions को call करके Janet values को manipulate किया जा सकता है
- इसे websites में embed भी किया जा सकता है, और Toodle की तरह custom programmable DSL वाली static sites बनाई जा सकती हैं
Text parsing और subprocess DSL
- Janet में text processing regular expressions की बजाय parsing expression grammar पर आधारित है
- parsing expression grammar regular expressions से अधिक सरल, शक्तिशाली और पूर्वानुमेय है, और line-based नहीं है, इसलिए multi-line text को parse कर सकती है
- यह HTML, JSON, और दूसरी non-regular languages को parse कर सकती है, और ऐसे binary file formats को भी संभाल सकती है जिनमें arbitrary null bytes हों
- sh एक third-party shell scripting DSL है जो pipes और redirection को सीधे Janet code के भीतर व्यक्त करने देता है
($ find . -name *.janet | say)
- यह DSL Janet को Perl के एक उचित विकल्प से आगे बढ़ाकर, काफी व्यापक प्रकार के programs के लिए Bash का भी एक उचित विकल्प बना देता है
Collections और syntax का एहसास
- Janet collection types mutable और immutable दोनों रूपों में आते हैं
- immutable collections में value semantics होती है, इसलिए immutable vector
[1 2] को (take 2 [1 2 3]) से अलग नहीं माना जाता, भले ही दोनों के memory addresses अलग हों
- mutable collections में reference semantics होती है, इसलिए hash table
@{:x 1 :y 2} सिर्फ अपने आप के बराबर होती है, और वही keys और values रखने वाली दूसरी hash table एक अलग object होती है
- syntax में parentheses का व्यापक उपयोग होता है, लेकिन lists के लिए
[] और tables के लिए {} का उपयोग करके रूप अलग किया जाता है
- mutable literals में हमेशा
@ prefix लगता है, जैसे @"mutable string"
- anonymous function को
(fn [x] (+ 1 x)) की तरह लिखा जाता है, और |(+ 1 $) की तरह expression को function में lift करने के लिए | shorthand भी दिया गया है
- splat या spread के लिए
; का उपयोग होता है, जैसे (+ ;args)
- backtick strings को जितने चाहें उतने backticks से खोला और उतने ही backticks से बंद किया जा सकता है, और backtick strings के भीतर
\n जैसी escape sequences लागू नहीं होतीं
- rest parameters को
. की जगह & से लिखा जाता है, जैसे (defn foo [first & rest] ...)
- Janet reader macro को support नहीं करता, इसलिए syntax खुद स्थिर रहती है, और अगर आप Janet पढ़ सकते हैं तो आप हर Janet program पढ़ सकते हैं
Macros और compile-time state
- Janet macros ऐसा code हैं जो code लिखता है, और compile time पर values और abstract syntax trees को manipulate करते हुए वर्तमान execution flow और भविष्य में चलने वाले application code flow दोनों से एक साथ काम करता है
- Janet macros hygienic नहीं हैं, और functions के लिए अलग namespace भी नहीं है
- लेकिन literal functions को unquote किया जा सकता है, इसलिए पूरी तरह referentially transparent macros लिखना संभव है
- जब Janet program compile होता है, तो top-level commands, सामान्य statements, और function declarations आदि को पहले execute किया जाता है, फिर program state का snapshot disk पर लिख दिया जाता है
- यह snapshot shared references को preserve करता है, इसलिए restart के बाद भी mutable values को बदलना जारी रखा जा सकता है
- generators अगली resume पर चलने वाला command याद रखते हैं, और closures भी अपने captured values बनाए रखते हैं
- macros compile-time code execution का एक special form हैं, लेकिन macros के बिना भी इस क्षमता का उपयोग किया जा सकता है
- games में splines को पहले से process किया जा सकता है, compile time पर files पढ़कर final binary में assets डाले जा सकते हैं, और arbitrary side effects भी किए जा सकते हैं
- Janet for Mortals ऐसा उदाहरण दिखाता है जिसमें SQL schema file के आधार पर database bindings अपने आप generate किए जाते हैं, और इसे अधिकांश languages में काफ़ी कठिन काम माना गया है
Lisp परंपरा से ज़्यादा सहज
- Janet पुरानी Lisp परंपराओं का ज्यों का त्यों पालन नहीं करता
CAR को first, PROGN को do, LAMBDA को fn, और SETQ को def कहा जाता है
nil खाली list नहीं, बल्कि एक स्वतंत्र type है, और booleans first-class values हैं
EQ, EQL, EQUAL, EQUALP जैसी श्रृंखला से बचा गया है, और linked lists भी लगभग दिखाई नहीं देतीं
2 टिप्पणियां
Hacker News की राय
Janet में कुछ कमियाँ हैं। खासकर package management version pinning की कमी है, और advanced HTTP routing जैसी लाइब्रेरी इकोसिस्टम भी कमज़ोर है
फिर भी यह बात बहुत पसंद है कि JPM से binaries और scripts बनाए जा सकते हैं और इसकी portability अच्छी है। पहले proof of concept के तौर पर Playdate game console पर Janet programming language चलाकर भी देखा था
मुझे Janet में code लिखना पसंद है, लेकिन हर बार लोग यह समझ लेते हैं कि यह language मैंने ही बनाई है, यह थोड़ा असहज होता है
“Janet writes Janet” वाला version भी देखना अच्छा रहेगा
यह dependencies को vendor करता है और
jpmके बिना भी modern Janet bundles को आसानी से install करने देता हैअगर आप LLM development के लिए खुले हैं, तो wrapper LLM से लिखवा सकते हैं और actual logic Janet में लिख सकते हैं
इसी developer की पहले की एक मिलती-जुलती language Fennel भी है। यह Lua में compile होती है और इसका implementation भी पूरा Lua में है
इसका अपना standard library नहीं है, इसलिए Janet की parser library जैसी कई अच्छी चीज़ें इसमें नहीं हैं, लेकिन Lua embedded environment में scripting के लिए यह अच्छी है
https://fennel-lang.org/
Fennel और Lua VM के बीच का connection बहुत नाज़ुक है, और Janet debugger व REPL की quality के आधे जितना भी नहीं पहुँचता। Fennel कहीं ज़्यादा portable है और LuaJIT की वजह से SBCL को पीछे भी छोड़ सकती है, इसलिए यह कमी और भी खलती है
लेकिन transpile experience पूरी तरह से पाँव खींच लेता है। workaround मौजूद हैं, लेकिन
debug.setinfoimplement कर देने पर भीmatchblock जैसे कम सुखद edge cases मिलते हैंमुझे लगता है कि LuaJIT2 को fork करके debugging और error structure को language transparency के ज़्यादा अनुकूल बनाना बहुत मूल्यवान होगा। तब Fennel जैसी languages कहीं ज़्यादा आकर्षक लगेंगी
लेखक ने पहले HN पर भी आए इन tools को Janet में बनाया था
https://bauble.studio
https://toodle.studio
इन दो दिलचस्प art tools की वजह से कुछ समय तक Janet को लेकर काफ़ी उत्साह रहा
Janet को ध्यान मिलता देखना हमेशा अच्छा लगता है। इसकी modern features में से एक sandbox का ज़िक्र करना चाहूँगा
“Interpreter को कुछ system resources इस्तेमाल करने से रोकने के लिए feature set disable कर दिए जाते हैं। एक बार disable हो जाने पर उन्हें फिर से enable करने का कोई तरीका नहीं है।”
https://janet-lang.org/api/misc.html#sandbox
“SETQ is def” देखकर पहले तो मैंने ज़ोर से “क्या?” कहा। वजह यह है कि SETQ binding बनाता नहीं, सिर्फ update करता है
documentation(https://janet-lang.org/docs/bindings.html) पढ़ने पर पता चला कि लेखक वास्तव में ग़लत था, और उसमें लिखा है कि “def से बनी bindings immutable होती हैं।” शायद उसका मतलब “SETQ is set” कहना था
Janet, Guile, Tcl और CL के बीच एक अच्छे संतुलन जैसा लगता है, इसलिए मैं इसे सच में पसंद करना चाहता हूँ, लेकिन lambda और control flow operators में square bracket vectors के इस्तेमाल से सहज रूप से एक तरह की अस्वीकृति महसूस होती है। Clojure के साथ भी यही दिक्कत है, लेकिन शायद काफ़ी कोशिश करूँ तो इसे पार कर पाऊँ
और यह भी जानना है कि अभी LSP/SLIME की स्थिति कैसी है। आजकल यह काफ़ी महत्वपूर्ण है
round brackets इस्तेमाल करने पर list का पहला element तय करता है कि बाकी list को कैसे interpret किया जाएगा। उदाहरण के लिए
(func a b c)function execution है,(macro x y z)macro expansion है, और([p q r] …)parameter vector से शुरू होकर उसके बाद execution forms वाले “bare” function body को दिखाता हैsquare brackets तब इस्तेमाल होते हैं जब elements एक ही “kind” के हों और पहला element special न हो। उदाहरण के लिए
(defn f [a b c] …)एक ही तरह के parameters का समूह है और पहला parameter special नहीं है, और(let [a 1 b 2] …)भी bindings का समूह है जिसमें पहली binding special नहीं हैजो एकमात्र exception याद आता है, वह
caseमें कई matching elements को group करना है, और वह convenience के लिए है। जब से यह तर्क समझ में आया, मेरा नज़रिया बदल गया, और उसके बाद यह सुंदर लगने लगा[1 2 3]की जगह(array 1 2 3)लिख सकते हैं, और(fn [x] (+ 1 x))की जगह(f (x) (+ 1 x))लिख सकते हैंयह अनिवार्य नहीं है
एक निश्चित लंबाई से बड़े system scripts में Janet मेरे लिए sh, Python, awk आदि की जगह लेता है
script start-up time बहुत तेज़ है, और मेरे system पर hyperfine के हिसाब से 1.4ms है, जो dash के 1ms के काफ़ी करीब है। यह compiled executable नहीं, बल्कि script के आधार पर है
sh-dslmodule की वजह से($ cmda w x | cmdb y z)जैसे shell commands को बहुत सुरुचिपूर्ण ढंग से लिखा जा सकता है। debugging के लिए image load कर पाने की सुविधा भी बहुत मददगार हैमैंने इसे बहुत हाल में इस्तेमाल करना शुरू किया है, लेकिन यह पहले ही मेरी पसंदीदा भाषाओं में से एक बनती दिख रही है, और इससे पहले मैंने जो दूसरी Lisp इस्तेमाल की थी वह सिर्फ SICP के लिए MIT Scheme थी
यह पोस्ट ताज़गीभरी है। इसमें इंटरनेट की AI-पूर्व चर्चाओं की गंध बची हुई है
नई language, नया syntax, और कई सालों से code लिखते आ रहे लोगों की तीखी बहसें हैं। अच्छा होगा अगर कोई ऐसा online community शुरू करे जहाँ AI की अनुमति न हो
दरअसल शायद उसे उससे ठीक पहले वाला relaunch कहना चाहिए, और अब लगता है कि फिर से एक नया homepage है। जो व्यक्ति online communities में AI को भरोसेमंद ढंग से रोकने का पहला तरीका खोज लेगा, उसके बहुत अमीर बनने की संभावना है
https://www.techspot.com/news/111698-digg-relaunch-fails-two...
किसी तरह का “मानवता का प्रमाण” एक कठिन समस्या है
admins ने जो सटीक नियम बनाया वह “meaningful human authorship” था, लेकिन भ्रम में मत रहिए। lobsters पर LLM के वैचारिक विरोधी बहुत हैं। technology को कितनी “meaningfully” लागू किया गया, यह ज़्यादा मायने नहीं रखता
मेरे काम को सिर्फ इसलिए कचरा मान लिया गया क्योंकि उसे AI ने छुआ था, और जब मैंने कहा कि मैं AI इस्तेमाल करता हूँ, तो कुछ लोगों ने मुझे exhibitionist या fetishist तक कहा। जो लोग जुड़ने का सोच रहे हों, उन्हें पहले से बता देना चाहता हूँ
“Janet literal functions को unquote करने देती है, इसलिए Janet में पूरी तरह referentially transparent macros लिखे जा सकते हैं” जैसे वाक्यों पर Lisp वाले सचमुच बहुत अमूर्त चीज़ों को लेकर उत्साहित होते लगते हैं
सड़क पर किसी आम इंसान से ऐसा कहें तो वह शायद भागने की कोशिश करेगा
#define MULTIPLY(x, y) x * yint result = MULTIPLY(2 + 3, 4); // 14सिर्फ इसलिए कि आपको किसी शब्द का मतलब नहीं पता, यह बुरा नहीं हो जाता। बात के ढंग से लगता है कि शायद उसका आशय यही था
programming में बार-बार आने वाले patterns और problems के लिए साझा भाषा होना अच्छी बात है। “वह programmers का समूह बड़ा अजीब है” कहकर terminology का मज़ाक उड़ाना निरर्थक है और उल्टा असर करता है
फिर से शुरू करने का सोच रहा हूँ, लेकिन यह भी सोच रहा हूँ कि niche features लागू करने लायक हैं भी या नहीं। मेरे लिए उन्हें implement करना भी मुश्किल है। शायद
dynamic-unwindको छोड़ देना, और संभव हैcall/ccभी हटा देना, फिर debuggability, ecosystem, performance, और package management पर ध्यान देना बेहतर होइसलिए मैं बहुत धुँधले ढंग से कह देता हूँ, “मैं computers से जुड़ा काम करता हूँ,” या फिर, “ज़्यादा दिलचस्प काम नहीं है,” और विषय बदलने की कोशिश करता हूँ। थोड़ा भी ज़्यादा specific होते ही लोग निकलने का रास्ता ढूँढने लगते हैं
ईमानदारी से कहूँ तो मुझे लगता है कि Lisp समुदायों के छोटे होने से उन्हें उल्टा फ़ायदा हुआ है। उदाहरण के लिए बहुत पुराने Design Patterns में भी inheritance पर composition को तरजीह देने की चेतावनी थी, फिर भी object-oriented programmers आज तक 15-स्तरीय गहरी hierarchies बना देते हैं
जब मैंने Janet को पहली बार देखा था, तब ये documents सचमुच मददगार थे
https://janetdocs.org/tutorials
https://janet.guide/ यह लेखक ने बनाया है
HN पर कभी-कभी आने वाली Janet posts की ओर मैं खिंचता रहा हूँ, लेकिन जिसे सब लोग बहुत सराहते हैं, वह Janet for Mortals मुझे बिल्कुल भी mortals के लिए किताब जैसा नहीं लगा
दूसरी languages की तुलना में Janet सचमुच सीखने में आसान है, इसलिए यह सुनकर हैरानी होती है कि वह किताब मुश्किल है। मैंने किताब नहीं पढ़ी, लेकिन language से कुछ हद तक परिचित हूँ, और सच कहूँ तो मेरी तरफ़ से सिर्फ प्रशंसा ही है
Janet तो Lisp 2.0 जैसा लगता है, इसलिए उसका syntax भी Lisp-जैसा है
Lobste.rs की राय
Janet शुरू किए 10 महीने ही हुए थे, और APL परिवार की भाषाओं को छोड़कर बाकी लगभग सब भूल जाने लायक इसमें डूब गया था; साथ ही कम्युनिटी docs site चला रहा हूँ और tutorial भी लिख रहा हूँ
शुरू के 3 हफ्तों के भीतर अपने सारे personal scripts फिर से लिख दिए, और जो नया ops software बना रहा था वह भी Janet में लिखा
implementation के स्तर पर Janet में लगभग पूरी भाषा hashmap की तरह काम करती है, इसलिए
(keys (curenv))से local symbols,(keys (getproto (curenv)))से core symbols देख सकते हैं; चाहें तो hashmap-आधारित CLOS जैसा कुछ भी बना सकते हैं, और उसका implementation भी हैJoy web framework से करीब 20 websites और कई services एक ही 512MB free VPS पर चला रहा हूँ, और उस पर tutorial भी लिखा है
लेकिन “immutable collections” कहना वास्तविकता से थोड़ा अलग है; standard library आम तौर पर mutable values लौटाती है, इसलिए अभी immutability पर ज़ोर देने की खास वजह नहीं है
compile-time values को runtime तक ले जाने वाली सुविधा खास तौर पर बहुत ताकतवर लगी। उदाहरण के लिए Bible
.tsvको compile करते समय hashmap के रूप में binary में embed कर दें, तो runtime पर सिर्फ lookup करना पड़ता है, औरembedइस्तेमाल करने वाले Go version से भी दो गुना तेज नतीजा मिलावही चीज़ Go में सीधे hashmap के रूप में implement करें तो कोड बहुत लंबा हो जाता, लेकिन Janet में Lisp to Go compiler सिर्फ 46 lines में बन गया
Ian Henry ने जो और दिलचस्प बात कही, वह यह थी कि Janet closure state को images/sessions के बीच preserve कर सकता है;
(curenv)hashmap में संबंधित environment सहेजकर नए REPL session में restore करने पर भी closure का internal state बना रहता हैLisp-आधारित music DSL https://lisp.trane.studio/ भी है, और paper https://dl.acm.org/doi/abs/10.1145/3677996.3678285 तथा output examples https://x.com/greg_ash/status/1824218993118388708 भी देखने लायक हैं
मेरी अपनी एक library भी है, जो कई data structures पर SQL-जैसी query syntax देती है
यह dataframe insert/update और CSV save/load को support करती है, साथ में Datalog और miniKanren भी शामिल हैं, और APL की तरह vectorized operations भी कर सकती है
Janet में सीधे J इस्तेमाल करने वाला jnj भी है, और Joy Web Framework में
(var account (db/find-by :account :where {:login (auth-result :login)}))जैसी DB query DSL है, जो असली website के auth code में भी इस्तेमाल होती है“immutable collections” सुनते ही persistent data structures याद आते हैं, लेकिन Janet में वह बुनियादी सुविधा नहीं है, भले ही वे उपयोगी हों
असल में जो चीज़ पसंद आती है, वह value types और reference types की symmetry है; लेख के आखिर में “immutable composite values” लिखा तो था, लेकिन अगर वह मैंने खुद न लिखा होता, तो शायद मैं भी उसका मतलब तुरंत न समझ पाता
Janet एक ताज़गी भरी और embed की जा सकने वाली language है, इसलिए इसे game engine जैसे projects में built-in scripting के लिए आज़माना चाहूँगा
जहाँ DLL बदलने बिना तेज iteration के लिए hot reload चाहिए, वहाँ यह अच्छी तरह फिट बैठती दिखती है; Lua भी बेहतरीन है, लेकिन कुछ मामलों में Janet ज़्यादा expressive लगती है
Janet एक language के रूप में वाकई शानदार है, और किसी दिन इसे Zig project की scripting language के रूप में इस्तेमाल करना चाहूँगा। अच्छा लगता है कि ज़्यादा लोग Janet का ज़िक्र कर रहे हैं
अच्छा तो लगता है, लेकिन मैं पहले से babashka के साथ Clojure scripting का आदी हो रहा हूँ, इसलिए यह कुछ वैसा ही महसूस होता है। embedability के अलावा क्या कोई बड़ा फ़ायदा है जो मैं मिस कर रहा हूँ, यह जानना चाहता हूँ
“rest args के साथ array destructuring से संभावित रूप से महँगी copy हो सकती है” जैसी बातें देखकर यह कुल मिलाकर कम functional लगती है, और यह बात मुझे पसंद नहीं आती
दूसरी languages में जब भी text parse करता हूँ, यह feature याद आती है
Janet किसी छोटे, embeddable Clojure से ज़्यादा, बेहतर functional programming support वाला Lua के करीब है