- Janet เป็นภาษาเชิง Lisp dialect ขนาดเล็ก แต่มีทั้งภาษาเชิงคำสั่ง, first-class functions, เนมสเปซตัวระบุแบบเดียว, และ lexical block scope จึงเริ่มต้นใช้งานได้อย่างเรียบง่าย
- แกนหลักของภาษา ประกอบด้วยคำสั่งเพียง 8 ตัวคือ
do, def, var, set, if, while, break, fn และ macro จะช่วยสร้างตัวครอบ control flow ที่ทรงพลังกว่าหรือสะดวกกว่า
- การแจกจ่ายแบบพร้อมใช้งาน ทำได้โดยคอมไพล์โปรแกรม Janet เป็นไฟล์ปฏิบัติการเนทีฟที่ลิงก์แบบสแตติกกับ Janet runtime จึงไม่ต้องให้ผู้ใช้ติดตั้ง Janet หรือ dependency ใด ๆ
- Parsing Expression Grammar (PEG) เรียบง่าย ทรงพลังกว่า และคาดเดาได้มากกว่า regex ขณะที่ sh ช่วยให้เขียน pipe และ redirection ภายใน Janet ได้ จึงขยายขอบเขตการเขียน CLI
- การรันตอนคอมไพล์ จะรันคำสั่งระดับบนสุดก่อน แล้วบันทึกสแนปช็อตสถานะของโปรแกรมลงดิสก์ ทำให้ส่งต่อค่า, shared references, generators และสถานะของ closures ไปยัง runtime ได้แม้ไม่ใช้ macro
แกนหลักที่เรียบง่าย
- Janet เป็นภาษาเชิงคำสั่ง และมี first-class functions, เนมสเปซตัวระบุแบบเดียว, และ lexical block scope
- แกนของภาษาถูกทำให้เล็กด้วยคำสั่งเพียง 8 ตัวคือ
do, def, var, set, if, while, break, fn
- macro ช่วยให้สร้างตัวครอบ control flow ระดับสูงที่ทรงพลังกว่าหรือสะดวกกว่าได้
- ความหมายเชิง runtime คุ้นเคย และส่วนที่เหลือของภาษาก็เล็กพอ ๆ กัน โดยทั้ง ไลบรารีมาตรฐาน อยู่ในหน้าเดียวได้
การแจกจ่ายแบบเนทีฟและการฝังใช้งาน
- โปรแกรม Janet สามารถคอมไพล์เป็นไฟล์ปฏิบัติการเนทีฟที่ลิงก์แบบสแตติกกับ Janet runtime ได้อย่างง่ายดาย
- ผู้ใช้ที่ได้รับไฟล์ไปไม่จำเป็นต้องติดตั้ง Janet, dependency ของโปรเจกต์ หรือองค์ประกอบอื่นเพิ่มเติม
- Janet จะคอมไพล์ตัวเองเป็น bytecode ก่อน แล้วเขียน bytecode นั้นลงในไฟล์
.c ที่ใช้เริ่ม Janet runtime จากนั้นคอมไพล์ไฟล์ C นั้นด้วย system C compiler
- ไบนารีเนทีฟ “hello world” แบบง่ายมีขนาดเล็กกว่า 1MB และใน Janet 1.27.0 บน aarch64 macOS มีขนาด 784K
- ไบนารีนี้บรรจุ Janet runtime ทั้งชุด, garbage collector และ bytecode compiler ไว้ด้วย จึงสร้างโปรแกรมที่ประเมินโค้ด Janet ตอน runtime ได้
- Janet runtime เป็นไลบรารี C ขนาดเล็ก จึงลิงก์เข้าไปแล้วเรียกฟังก์ชัน C ปกติเพื่อจัดการค่า Janet ได้
- ยังฝังลงในเว็บไซต์ได้ด้วย และสามารถสร้าง static site ที่มี programmable DSL แบบกำหนดเองได้ เช่น Toodle
การพาร์สข้อความและ DSL สำหรับซับโปรเซส
- การจัดการข้อความของ Janet อิงกับ Parsing Expression Grammar แทน regex
- Parsing Expression Grammar เรียบง่าย ทรงพลังกว่า และคาดเดาได้มากกว่า regex อีกทั้งไม่ผูกกับระดับบรรทัด จึงพาร์สข้อความหลายบรรทัดได้
- สามารถพาร์ส HTML, JSON และภาษา non-regular อื่น ๆ รวมถึงจัดการรูปแบบไฟล์ไบนารีที่มี null byte แบบใดก็ได้
- sh คือ third-party shell scripting DSL ที่ทำให้เขียน pipe และ redirection ได้โดยตรงในโค้ด Janet
($ find . -name *.janet | say)
- DSL นี้ยกระดับ Janet จากการเป็นทางเลือกที่สมเหตุสมผลแทน Perl ไปสู่การเป็นทางเลือกที่สมเหตุสมผลแทน Bash สำหรับโปรแกรมในขอบเขตที่กว้างพอสมควร
คอลเลกชันและสัมผัสด้านไวยากรณ์
- ชนิดคอลเลกชันของ Janet มีทั้งแบบ mutable และ immutable
- คอลเลกชัน immutable มี value semantics ดังนั้นเวกเตอร์ immutable
[1 2] จึงไม่ต่างจาก (take 2 [1 2 3]) แม้จะอยู่คนละที่อยู่หน่วยความจำ
- คอลเลกชัน mutable มี reference semantics ดังนั้น hash table
@{:x 1 :y 2} จึงเท่ากับตัวมันเองเท่านั้น และ hash table อื่นที่มีคีย์กับค่าเดียวกันก็ยังเป็นคนละออบเจ็กต์
- ไวยากรณ์ใช้วงเล็บอย่างกว้างขวาง แต่ใช้
[] สำหรับลิสต์ และ {} สำหรับตารางเพื่อแยกรูปแบบ
- literal แบบ mutable จะมีคำนำหน้า
@ เสมอ เช่น @"mutable string"
- ฟังก์ชัน anonymous เขียนเป็น
(fn [x] (+ 1 x)) และยังมีรูปย่อที่ยก expression ให้เป็นฟังก์ชันด้วย | เช่น |(+ 1 $)
- splat หรือ spread ใช้
; จึงเขียนได้แบบ (+ ;args)
- สตริงแบบ backtick สามารถเปิดด้วย backtick จำนวนเท่าใดก็ได้ และปิดด้วยจำนวนเท่ากัน โดยภายในสตริง backtick จะไม่ใช้ escape sequence อย่าง
\n
- rest parameter ใช้
& แทน . จึงเขียนได้แบบ (defn foo [first & rest] ...)
- Janet ไม่รองรับ reader macro ทำให้ตัวไวยากรณ์คงที่ และถ้าอ่าน Janet ได้ ก็อ่านทุกโปรแกรม Janet ได้
Macro และสถานะตอนคอมไพล์
- macro ของ Janet คือโค้ดที่เขียนโค้ด โดยในช่วงคอมไพล์จะจัดการทั้งค่ากับ abstract syntax tree และดูแลพร้อมกันทั้ง flow ของโค้ดที่กำลังรันอยู่ตอนนี้กับ flow ของโค้ดแอปพลิเคชันที่จะรันในอนาคต
- macro ของ Janet ไม่เป็นแบบ hygienic และไม่มีเนมสเปซแยกสำหรับฟังก์ชัน
- แต่สามารถ unquote ฟังก์ชันแบบ literal ได้ จึงเขียน macro ที่ referentially transparent อย่างสมบูรณ์ได้
- เมื่อคอมไพล์โปรแกรม Janet ระบบจะรันคำสั่งระดับบนสุด, statement ทั่วไป และการประกาศฟังก์ชันก่อน แล้วจึงบันทึกสแนปช็อตของสถานะโปรแกรมลงดิสก์
- สแนปช็อตนี้รักษา shared references ไว้ จึงกลับมาเริ่มใหม่แล้วแก้ไขค่า mutable ต่อได้
- generator จะจำคำสั่งที่จะรันเมื่่อ resume ครั้งถัดไป และ closure ก็ยังเก็บค่าที่ปิดไว้ได้
- macro เป็นรูปแบบพิเศษของการรันโค้ดตอนคอมไพล์ แต่ความสามารถนี้ใช้ได้แม้ไม่พึ่ง macro
- ในเกมสามารถ pre-process spline ล่วงหน้าได้, อ่านไฟล์ตอนคอมไพล์เพื่อนำ asset ใส่ลงในไบนารีสุดท้ายได้, และยังทำ side effect ใด ๆ ก็ได้
- Janet for Mortals แสดงตัวอย่างการสร้าง database binding อัตโนมัติจากไฟล์ SQL schema และมองว่างานแบบนี้ค่อนข้างยากในภาษาส่วนใหญ่
สบายใจกว่าธรรมเนียม Lisp แบบดั้งเดิม
- Janet ไม่ได้ยึดตามธรรมเนียม Lisp เก่า ๆ แบบตรงตัว
CAR ถูกตั้งชื่อเป็น first, PROGN เป็น do, LAMBDA เป็น fn, และ SETQ เป็น def
nil ไม่ใช่ลิสต์ว่าง แต่เป็นชนิดข้อมูลแยกต่างหาก และ boolean ก็เป็นค่าระดับ first-class
- หลีกเลี่ยงตระกูล
EQ, EQL, EQUAL, EQUALP และแทบไม่เห็น linked list
2 ความคิดเห็น
ความคิดเห็นจาก Hacker News
Janet มีจุดที่น่าเสียดายอยู่บ้าง โดยหลัก ๆ คือ การระบุเวอร์ชันในการจัดการแพ็กเกจ ยังไม่ดีพอ และยังขาดไลบรารีหลายด้าน เช่น HTTP routing ขั้นสูง
ถึงอย่างนั้น ผมชอบมากที่สามารถใช้ JPM สร้างทั้งไบนารีและสคริปต์ได้ และยังพกพาได้ดีมาก ก่อนหน้านี้ผมเคยลองพอร์ตภาษา Janet ไปลงบนเครื่องเกม Playdate เป็น proof of concept ด้วย
ผมสนุกกับการเขียนโค้ดด้วย Janet แต่ทุกครั้งก็ลำบากนิดหน่อยที่คนมักคิดว่าผมเป็นคนสร้างภาษานี้
น่าลองทำเวอร์ชัน “Janet เขียน Janet” ดูเหมือนกัน
มันช่วย vendor dependencies และทำให้ติดตั้ง Janet bundle แบบสมัยใหม่ได้ง่ายแม้ไม่มี jpm
ถ้าเปิดรับการพัฒนาด้วย LLM ก็ให้ LLM เขียน wrapper แล้วค่อยเขียน logic จริงด้วย Janet ได้
มีอีกภาษาที่คล้ายกันซึ่งผู้พัฒนาคนเดียวกันทำไว้ก่อนหน้านี้คือ Fennel มันคอมไพล์ไปเป็น Lua และตัว implementation เองก็เขียนด้วย Lua ทั้งหมด
มันไม่มี standard library ของตัวเอง เลยขาดของดีหลายอย่างอย่าง parser library ของ Janet แต่ก็เหมาะมากสำหรับการเขียนสคริปต์ในสภาพแวดล้อมที่ฝัง Lua ไว้
https://fennel-lang.org/
จุดเชื่อมระหว่าง Fennel กับ Lua VM เปราะบางมาก และคุณภาพยังห่างจาก debugger กับ REPL ของ Janet มากกว่าครึ่ง Fennel พกพาได้ดีกว่ามาก และด้วย LuaJIT ก็อาจแรงแซง SBCL ได้ด้วย เลยยิ่งน่าเสียดาย
แต่ประสบการณ์จากการ transpile นี่แหละที่ฉุดไว้เต็ม ๆ แม้จะมีทางเลี่ยงอยู่บ้าง แต่ต่อให้ implement
debug.setinfoก็ยังต้องเจอกับ edge case ที่ไม่น่ารื่นรมย์อย่างmatchblock อยู่ดีผมคิดว่าการ fork LuaJIT2 แล้วแก้โครงสร้างการดีบักและข้อผิดพลาดให้สอดคล้องกับ language transparency มากขึ้นนั้นมีคุณค่ามาก แบบนั้นภาษาพวก Fennel จะดูน่าสนใจกว่านี้มาก
ผู้เขียนเคยสร้างเครื่องมือเหล่านี้ด้วย Janet ซึ่งก่อนหน้านี้ก็เคยถูกพูดถึงบน HN เช่นกัน
https://bauble.studio
https://toodle.studio
เครื่องมือศิลปะที่น่าสนใจสองตัวนี้ทำให้ผมคาดหวังกับ Janet อยู่พักหนึ่งเหมือนกัน
ผมดีใจเสมอเวลาที่เห็น Janet ได้รับความสนใจ หนึ่งในฟีเจอร์สมัยใหม่ที่อยากยกขึ้นมาคือ sandbox
“ปิดการใช้งานชุดความสามารถเพื่อไม่ให้อินเทอร์พรีเตอร์ใช้ทรัพยากรระบบบางอย่างได้ เมื่อปิดแล้วจะไม่มีทางเปิดกลับได้อีก”
https://janet-lang.org/api/misc.html#sandbox
ตอนเห็น “SETQ is def” ครั้งแรก ผมเผลออุทานออกมาดัง ๆ ว่า “อะไรนะ?” เพราะ SETQ ไม่ได้สร้าง binding แต่มันแค่อัปเดตเท่านั้น
พอไปอ่านเอกสาร(https://janet-lang.org/docs/bindings.html) ก็พบว่าผู้เขียนเข้าใจผิดจริง ๆ เพราะมีเขียนไว้ว่า “binding ที่สร้างด้วย def เป็น immutable” น่าจะตั้งใจจะพูดว่า “SETQ is set” มากกว่า
ผมชอบมากที่ Janet ดูเหมือนจุดกึ่งกลางที่ลงตัวระหว่าง Guile, Tcl และ CL แต่ก็มีแรงต้านในใจอย่างรุนแรงกับการใช้ เวกเตอร์วงเล็บเหลี่ยม สำหรับ lambda และตัวดำเนินการควบคุมการไหล Clojure ก็เหมือนกัน เป็นจุดที่ข้ามผ่านได้ยากมาก แต่ถ้าพยายามมากพอก็อาจจะไหว
แล้วก็อยากรู้เหมือนกันว่าตอนนี้สถานะของ LSP/SLIME เป็นอย่างไร เพราะทุกวันนี้มันสำคัญมาก
ถ้าใช้วงเล็บกลม องค์ประกอบตัวแรกของลิสต์จะเป็นตัวกำหนดว่าจะตีความส่วนที่เหลือของลิสต์อย่างไร เช่น
(func a b c)คือการเรียกฟังก์ชัน,(macro x y z)คือการขยายแมโคร, และ([p q r] …)คือฟังก์ชันแบบ “เปลือย” ที่เริ่มด้วยเวกเตอร์พารามิเตอร์แล้วตามด้วย expression ที่จะรันส่วนวงเล็บเหลี่ยมใช้เมื่อองค์ประกอบต่าง ๆ เป็น “ชนิด” เดียวกันและตัวแรกไม่ได้พิเศษไปกว่าตัวอื่น เช่น
(defn f [a b c] …)คือกลุ่มพารามิเตอร์ชนิดเดียวกันที่ตัวแรกไม่ได้พิเศษ และ(let [a 1 b 2] …)ก็เป็นกลุ่มของ binding ที่ตัวแรกไม่ได้พิเศษเช่นกันข้อยกเว้นเดียวที่นึกออกคือการใช้ใน
caseเพื่อจับกลุ่มองค์ประกอบที่ใช้ match หลายตัว แต่ก็เป็นเรื่องของความสะดวก พอผมเข้าใจตรรกะนี้ ความเห็นก็เปลี่ยนไป และหลังจากนั้นก็เริ่มรู้สึกว่ามันสวยงาม(array 1 2 3)แทน[1 2 3]และเขียน(f (x) (+ 1 x))แทน(fn [x] (+ 1 x))มันไม่ใช่ข้อบังคับ
สำหรับสคริปต์ระบบที่ยาวเกินระดับหนึ่ง Janet ได้เข้ามาแทนที่ sh, Python, awk ฯลฯ สำหรับผม
เวลาเริ่มรันสคริปต์เร็วมาก และบนระบบของผมวัดด้วย hyperfine ได้ 1.4ms ซึ่งใกล้เคียงกับ 1ms ของ dash นี่พูดถึงสคริปต์ ไม่ใช่ไฟล์ปฏิบัติการที่คอมไพล์แล้ว
ด้วยโมดูล
sh-dslเลยเขียนคำสั่งเชลล์แบบ($ cmda w x | cmdb y z)ได้อย่างสง่างามมาก และความสามารถในการโหลดอิมเมจเพื่อดีบักก็ช่วยได้มากเช่นกันเพิ่งเริ่มใช้ได้ไม่นานมาก แต่ดูแล้วน่าจะกลายเป็นหนึ่งในภาษาที่ผมชอบที่สุดแล้ว และ Lisp อื่นที่เคยใช้ก่อนหน้านี้ก็มีแค่ MIT Scheme สำหรับ SICP
โพสต์นี้ให้ความรู้สึกสดใหม่ดี มีกลิ่นอายของ การถกเถียงก่อนยุค AI บนอินเทอร์เน็ต
มีทั้งภาษาใหม่ ไวยากรณ์ใหม่ และการถกเถียงดุเดือดของคนที่เขียนโค้ดกันมาหลายปี อยากให้มีใครสักคนเริ่มคอมมูนิตี้ออนไลน์ที่ไม่อนุญาต AI
จริง ๆ คงต้องเรียกว่าการเปิดตัวใหม่ครั้งก่อน และตอนนี้เหมือนจะมีหน้าเว็บใหม่อีกแล้ว ใครก็ตามที่หาวิธีกัน AI ออกจากคอมมูนิตี้ออนไลน์ได้อย่างน่าเชื่อถือเป็นคนแรก น่าจะมีโอกาสรวยมาก
https://www.techspot.com/news/111698-digg-relaunch-fails-two...
การพิสูจน์ความเป็นมนุษย์บางรูปแบบเป็นปัญหาที่แก้ยาก
กฎที่ทีมดูแลตั้งไว้จริง ๆ คือ “งานเขียนโดยมนุษย์อย่างมีนัยสำคัญ” แต่อย่าหลงไปกับถ้อยคำนี้ ใน lobsters มีคนจำนวนมากที่คัดค้าน LLM ในเชิงอุดมการณ์ เรื่องที่เทคโนโลยีถูกใช้อย่าง “มีนัยสำคัญ” แค่ไหนไม่ได้สำคัญมากนัก
งานของผมถูกจัดว่าเป็นขยะเพียงเพราะ AI เคยแตะต้องมัน และก็มีคนเรียกผมว่าเป็นพวกชอบเปิดเผยตัวเองหรือพวกมีเฟติชเพียงเพราะผมบอกว่าใช้ AI เลยอยากเตือนไว้ล่วงหน้าสำหรับคนที่คิดจะสมัคร
พอเจอประโยคอย่าง “การอนุญาตให้ unquote ฟังก์ชันลิเทอรัลทำให้ Janet สามารถเขียนแมโครที่โปร่งใสต่อการอ้างอิงได้อย่างสมบูรณ์” ก็รู้สึกว่าคนสาย Lisp นี่ตื่นเต้นกับเรื่องนามธรรมมากจริง ๆ
ถ้าพูดแบบนี้กับคนทั่วไปบนถนน เขาคงวิ่งหนี
#define MULTIPLY(x, y) x * yint result = MULTIPLY(2 + 3, 4); // 14การไม่รู้ว่าคำบางคำหมายถึงอะไรไม่ได้แปลว่าสิ่งนั้นแย่ จากถ้อยคำที่ใช้ ดูเหมือนเขาน่าจะหมายถึงอย่างนั้น
การมีภาษาร่วมกันไว้พูดถึงรูปแบบและปัญหาที่เกิดซ้ำในงานเขียนโปรแกรมเป็นเรื่องดี การเอาศัพท์ไปล้อว่า “พวกโปรแกรมเมอร์กลุ่มนั้นประหลาดจริง” ไม่มีประโยชน์และมีแต่จะให้ผลย้อนกลับ
ตอนนี้กำลังคิดว่าจะกลับไปทำต่อดีไหม และกำลังชั่งใจว่าฟีเจอร์เฉพาะทางเหล่านั้นคุ้มค่ากับการทำหรือเปล่า สำหรับผมมันก็ทำยากเหมือนกัน อาจจะข้าม
dynamic-unwindไป แล้วอาจตัดcall/ccทิ้งด้วย จากนั้นไปโฟกัสเรื่องการดีบักได้ ระบบนิเวศ ประสิทธิภาพ และการจัดการแพ็กเกจน่าจะดีกว่าเลยพยายามตอบกว้าง ๆ ว่า “ทำงานเกี่ยวกับคอมพิวเตอร์” หรือไม่ก็บอกว่า “ไม่ใช่งานที่น่าสนใจเท่าไร” แล้วเปลี่ยนเรื่อง ถ้าลงรายละเอียดมากกว่านั้นอีกนิด คนก็เริ่มมองหาทางออกแล้ว
พูดตามตรง ผมคิดว่าความที่คอมมูนิตี้สาย Lisp มีขนาดเล็กกลับเป็นข้อดีด้วยซ้ำ ยกตัวอย่างเช่น แม้แต่ Design Patterns รุ่นเก่ามากก็ยังเตือนให้เลือก composition มากกว่า inheritance แต่โปรแกรมเมอร์สาย OOP ก็ยังสร้างลำดับชั้นลึก 15 ชั้นกันอยู่ดี
ตอนที่ผมเพิ่งเริ่มรู้จัก Janet เอกสารพวกนี้ช่วยได้มากจริง ๆ
https://janetdocs.org/tutorials
https://janet.guide/ ทำโดยผู้เขียนโพสต์นี้
ผมเคยถูกดึงดูดทุกครั้งที่มีโพสต์เกี่ยวกับ Janet ขึ้นบน HN แต่ Janet for Mortals ที่ทุกคนชมกันมากนั้น กลับไม่รู้สึกว่าเป็นหนังสือสำหรับมนุษย์เดินดินเลยสักนิด
ถึงจะเป็น Lisp แต่พื้นผิวของภาษานั้นเล็กมาก เมื่อเทียบกับภาษาอื่น ๆ แล้ว Janet จัดว่าเรียนรู้ง่ายจริง ๆ เลยแปลกใจที่หนังสือเล่มนั้นยาก ผมไม่ได้อ่านหนังสือเล่มนั้น แต่คุ้นกับภาษานี้พอสมควร และพูดตามตรงก็มีแต่คำชม
Janet ดูเหมือน Lisp 2.0 เพราะงั้นไวยากรณ์ก็เป็นสไตล์ Lisp
ความคิดเห็นจาก Lobste.rs
หลังจากเริ่มใช้ Janet ได้เพียง 10 เดือน ก็อินมากจนแทบลืมภาษาอื่นเกือบหมดนอกจากสาย APL และกำลังดูแล เว็บไซต์เอกสารของชุมชน พร้อมทั้งเขียน บทสอน อยู่ด้วย
ภายใน 3 สัปดาห์แรกก็เขียนสคริปต์ส่วนตัวทั้งหมดใหม่ และซอฟต์แวร์ฝั่งปฏิบัติการที่ทำขึ้นใหม่ก็เขียนด้วย Janet
ในเชิง implementation, Janet ทั้งภาษาทำงานแทบจะเหมือน hash map ดังนั้นจึงดู local symbol ได้ด้วย
(keys (curenv))และดู core symbol ได้ด้วย(keys (getproto (curenv))); ถ้าต้องการก็สามารถสร้างสิ่งคล้าย CLOS บนพื้นฐาน hash map ได้ และก็มี implementation อยู่แล้วใช้ Joy web framework รันเว็บไซต์ราว 20 แห่งกับอีกหลาย service บน VPS ฟรี 512MB เพียงเครื่องเดียว และยังเขียน บทสอน ที่เกี่ยวข้องไว้ด้วย
แต่คำว่า “immutable collection” นั้นค่อนข้างคลาดเคลื่อนจากความเป็นจริง เพราะ standard library โดยมากคืนค่าแบบ mutable จึงยังไม่มีเหตุผลมากนักที่จะยึดความเป็น immutable ในตอนนี้
ความสามารถในการส่งค่าจากช่วง compile time ไปยัง runtime นั้นทรงพลังมาก ตัวอย่างเช่น ถ้าใส่ไฟล์พระคัมภีร์
.tsvลงในไบนารีเป็น hash map ตั้งแต่ตอนคอมไพล์ ตอนรันก็เหลือแค่ lookup และยังได้ผลลัพธ์ เร็วกว่า เวอร์ชัน Go ที่ใช้embedถึงสองเท่าถ้าจะทำแบบเดียวกันใน Go ด้วย hash map โดยตรง โค้ดจะยาวกว่ามาก แต่ใน Janet กลับทำ Lisp to Go compiler ได้ใน 46 บรรทัด
อีกจุดที่ Ian Henry บอกว่าน่าสนใจกว่าคือ Janet สามารถ คงสถานะของ closure ข้าม image/session ได้ โดยเก็บ environment ที่เกี่ยวข้องไว้ใน hash map ของ
(curenv)แล้วนำกลับมา restore ใน REPL session ใหม่ ก็ยังต่อสถานะภายใน closure เดิมได้ยังมี https://lisp.trane.studio/ ซึ่งเป็น music DSL แบบ Lisp พร้อมทั้งงานวิจัย https://dl.acm.org/doi/abs/10.1145/3677996.3678285 และตัวอย่างผลลัพธ์ https://x.com/greg_ash/status/1824218993118388708 ที่น่าดู
ยังมีไลบรารีที่ทำเองซึ่งให้ syntax สำหรับ query คล้าย SQL บน data structure หลายแบบ
รองรับการ insert/update dataframe และการบันทึก/โหลด CSV รวมถึงมี Datalog กับ miniKanren และยังทำ vectorized operation แบบ APL ได้ด้วย
นอกจากนี้ยังมี jnj ที่ใช้ J ตรง ๆ ใน Janet และใน Joy Web Framework ก็มี DB query DSL อย่าง
(var account (db/find-by :account :where {:login (auth-result :login)}))ซึ่งถูกใช้ใน โค้ดยืนยันตัวตนของเว็บไซต์จริง ด้วยคำว่า “immutable collection” ชวนให้นึกถึง persistent data structure ซึ่งแม้จะมีประโยชน์ แต่ไม่ใช่ความสามารถพื้นฐานของ Janet
สิ่งที่ชอบจริง ๆ คือความสมมาตรระหว่าง value type กับ reference type และแม้ตอนท้ายบทความจะเขียนว่า “immutable composite values” แต่ถ้าไม่ใช่ข้อความที่ตัวเองเขียน ก็คงเดาความหมายได้ไม่ทันทีเหมือนกัน
Janet เป็นภาษาที่สดใหม่และ embed ได้ เลยอยากลองเอาไปใช้เป็นสคริปต์ภายในโปรเจกต์อย่าง game engine
ดูเหมาะกับงานที่ต้องการ hot reload เพื่อวนรอบพัฒนาได้เร็วโดยไม่ต้องสลับ DLL; Lua ก็ยอดเยี่ยม แต่ Janet ดูมี expressive power ดีกว่าในบางด้าน
Janet ในฐานะภาษานั้นเจ๋งมาก และสักวันหนึ่งอยากลองใช้เป็น ภาษาสคริปต์ ในโปรเจกต์ Zig ดีใจที่เห็นคนพูดถึง Janet มากขึ้น
มันดูดีอยู่หรอก แต่ตอนนี้กำลังคุ้นกับการเขียนสคริปต์ Clojure ผ่าน babashka แล้ว เลยให้ความรู้สึกคล้ายกัน อยากรู้ว่านอกจากเรื่อง การ embed ได้ ยังมีข้อดีใหญ่อื่นที่อาจมองข้ามอยู่ไหม
ส่วนที่บอกว่า “การ destructure array ที่มี rest argument อาจทำให้เกิดการคัดลอกที่มีต้นทุนสูง” ทำให้รู้สึกว่ามันโดยรวมไม่ค่อยเป็นสาย functional เท่าไร ซึ่งไม่ค่อยถูกใจ
เป็นฟีเจอร์ที่พอไป parse ข้อความในภาษาอื่นเมื่อไรก็จะคิดถึงทันที
Janet ไม่ได้ใกล้กับ Clojure ฉบับเล็กที่ embed ได้มากนัก แต่ใกล้กับ Lua ที่รองรับ functional programming ได้ดีกว่า มากกว่า