1 คะแนน โดย GN⁺ 2 시간 전 | 2 ความคิดเห็น | แชร์ทาง WhatsApp
  • 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 ความคิดเห็น

 
GN⁺ 1 시간 전
ความคิดเห็นจาก Hacker News
  • Janet มีจุดที่น่าเสียดายอยู่บ้าง โดยหลัก ๆ คือ การระบุเวอร์ชันในการจัดการแพ็กเกจ ยังไม่ดีพอ และยังขาดไลบรารีหลายด้าน เช่น HTTP routing ขั้นสูง
    ถึงอย่างนั้น ผมชอบมากที่สามารถใช้ JPM สร้างทั้งไบนารีและสคริปต์ได้ และยังพกพาได้ดีมาก ก่อนหน้านี้ผมเคยลองพอร์ตภาษา Janet ไปลงบนเครื่องเกม Playdate เป็น proof of concept ด้วย
    ผมสนุกกับการเขียนโค้ดด้วย Janet แต่ทุกครั้งก็ลำบากนิดหน่อยที่คนมักคิดว่าผมเป็นคนสร้างภาษานี้

    • Julia Evans มีบทความสนุก ๆ ที่ทำภาพการทำงานของ Gunzip ด้วย Julia: https://jvns.ca/blog/2013/10/24/day-16-gzip-plus-poetry-equa...
      น่าลองทำเวอร์ชัน “Janet เขียน Janet” ดูเหมือนกัน
    • สงสัยว่าเคยลอง jeep หรือยัง: https://github.com/pyrmont/jeep/
      มันช่วย vendor dependencies และทำให้ติดตั้ง Janet bundle แบบสมัยใหม่ได้ง่ายแม้ไม่มี jpm
    • ถ้าต้องการฝั่งเซิร์ฟเวอร์ ก็มี joy ที่ทำ HTTP server ขนาดเบาเองอย่างที่ veqq พูดไว้ ส่วนถ้าต้องการไคลเอนต์ การห่อ libcurl หรือ HTTP client ตัวอื่นด้วย Janet C API ก็ทำได้ค่อนข้างง่าย
      ถ้าเปิดรับการพัฒนาด้วย LLM ก็ให้ LLM เขียน wrapper แล้วค่อยเขียน logic จริงด้วย Janet ได้
    • อยากรู้ว่า HTTP routing ขั้นสูง ที่ว่าหมายถึงอะไรโดยเฉพาะ ผมทำงานด้านเว็บทั้งหมดด้วย https://github.com/joy-framework/joy อยู่แล้ว ดังนั้นถ้ามีฟีเจอร์ไหนขาดไป ก็น่าจะใส่เพิ่มได้ภายในสัปดาห์เดียว
  • มีอีกภาษาที่คล้ายกันซึ่งผู้พัฒนาคนเดียวกันทำไว้ก่อนหน้านี้คือ Fennel มันคอมไพล์ไปเป็น Lua และตัว implementation เองก็เขียนด้วย Lua ทั้งหมด
    มันไม่มี standard library ของตัวเอง เลยขาดของดีหลายอย่างอย่าง parser library ของ Janet แต่ก็เหมาะมากสำหรับการเขียนสคริปต์ในสภาพแวดล้อมที่ฝัง Lua ไว้
    https://fennel-lang.org/

    • Fennel ดีมากจริง ๆ และยังเป็นวิธีที่ดีในการเริ่มต้นกับสาย Clojure ด้วย ข้อบ่นใหญ่ที่สุดของผมคือการดีบักเป็น กับระเบิดของการ transpile แบบคลาสสิก
      จุดเชื่อมระหว่าง Fennel กับ Lua VM เปราะบางมาก และคุณภาพยังห่างจาก debugger กับ REPL ของ Janet มากกว่าครึ่ง Fennel พกพาได้ดีกว่ามาก และด้วย LuaJIT ก็อาจแรงแซง SBCL ได้ด้วย เลยยิ่งน่าเสียดาย
      แต่ประสบการณ์จากการ transpile นี่แหละที่ฉุดไว้เต็ม ๆ แม้จะมีทางเลี่ยงอยู่บ้าง แต่ต่อให้ implement debug.setinfo ก็ยังต้องเจอกับ edge case ที่ไม่น่ารื่นรมย์อย่าง match block อยู่ดี
      ผมคิดว่าการ fork LuaJIT2 แล้วแก้โครงสร้างการดีบักและข้อผิดพลาดให้สอดคล้องกับ language transparency มากขึ้นนั้นมีคุณค่ามาก แบบนั้นภาษาพวก Fennel จะดูน่าสนใจกว่านี้มาก
  • ผู้เขียนเคยสร้างเครื่องมือเหล่านี้ด้วย Janet ซึ่งก่อนหน้านี้ก็เคยถูกพูดถึงบน HN เช่นกัน
    https://bauble.studio
    https://toodle.studio
    เครื่องมือศิลปะที่น่าสนใจสองตัวนี้ทำให้ผมคาดหวังกับ Janet อยู่พักหนึ่งเหมือนกัน

  • ผมดีใจเสมอเวลาที่เห็น Janet ได้รับความสนใจ หนึ่งในฟีเจอร์สมัยใหม่ที่อยากยกขึ้นมาคือ sandbox
    “ปิดการใช้งานชุดความสามารถเพื่อไม่ให้อินเทอร์พรีเตอร์ใช้ทรัพยากรระบบบางอย่างได้ เมื่อปิดแล้วจะไม่มีทางเปิดกลับได้อีก”
    https://janet-lang.org/api/misc.html#sandbox

    • เป็นฟีเจอร์ที่เจ๋งมากจริง ๆ แต่ก็สงสัยว่าสถานการณ์แบบไหนที่โปรแกรมเมอร์ทั่วไปจะต้องการ การทำ sandboxing แบบนี้
  • ตอนเห็น “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 เป็นอย่างไร เพราะทุกวันนี้มันสำคัญมาก

    • การใช้ วงเล็บเหลี่ยม ในไวยากรณ์ของ Clojure นั้นสม่ำเสมอมากและค่อนข้างมีเหตุผล
      ถ้าใช้วงเล็บกลม องค์ประกอบตัวแรกของลิสต์จะเป็นตัวกำหนดว่าจะตีความส่วนที่เหลือของลิสต์อย่างไร เช่น (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))
      มันไม่ใช่ข้อบังคับ
    • ถ้าเข้าใจว่า destructuring ใน Clojure ทำงานอย่างไร ก็จะเห็นชัดว่าวงเล็บเหลี่ยมมีบทบาทอะไร
  • สำหรับสคริปต์ระบบที่ยาวเกินระดับหนึ่ง Janet ได้เข้ามาแทนที่ sh, Python, awk ฯลฯ สำหรับผม
    เวลาเริ่มรันสคริปต์เร็วมาก และบนระบบของผมวัดด้วย hyperfine ได้ 1.4ms ซึ่งใกล้เคียงกับ 1ms ของ dash นี่พูดถึงสคริปต์ ไม่ใช่ไฟล์ปฏิบัติการที่คอมไพล์แล้ว
    ด้วยโมดูล sh-dsl เลยเขียนคำสั่งเชลล์แบบ ($ cmda w x | cmdb y z) ได้อย่างสง่างามมาก และความสามารถในการโหลดอิมเมจเพื่อดีบักก็ช่วยได้มากเช่นกัน
    เพิ่งเริ่มใช้ได้ไม่นานมาก แต่ดูแล้วน่าจะกลายเป็นหนึ่งในภาษาที่ผมชอบที่สุดแล้ว และ Lisp อื่นที่เคยใช้ก่อนหน้านี้ก็มีแค่ MIT Scheme สำหรับ SICP

    • สำหรับผม babashka เข้ามาแทน sh, Python, awk ฯลฯ
  • โพสต์นี้ให้ความรู้สึกสดใหม่ดี มีกลิ่นอายของ การถกเถียงก่อนยุค AI บนอินเทอร์เน็ต
    มีทั้งภาษาใหม่ ไวยากรณ์ใหม่ และการถกเถียงดุเดือดของคนที่เขียนโค้ดกันมาหลายปี อยากให้มีใครสักคนเริ่มคอมมูนิตี้ออนไลน์ที่ไม่อนุญาต AI

    • ถ้าไม่ได้ตามข่าวช่วงหลัง การเปิดตัวใหม่ล่าสุดของ digg.com ล้มเหลวเพราะรับมือกับ การถาโถมของบอต ไม่ไหว
      จริง ๆ คงต้องเรียกว่าการเปิดตัวใหม่ครั้งก่อน และตอนนี้เหมือนจะมีหน้าเว็บใหม่อีกแล้ว ใครก็ตามที่หาวิธีกัน AI ออกจากคอมมูนิตี้ออนไลน์ได้อย่างน่าเชื่อถือเป็นคนแรก น่าจะมีโอกาสรวยมาก
      https://www.techspot.com/news/111698-digg-relaunch-fails-two...
    • ผมคิดบ่อย ๆ ว่าจะสร้างคอมมูนิตี้ออนไลน์ที่ไม่อนุญาต AI ได้อย่างไร โดยเฉพาะถ้าจะทำโดยไม่ทำลาย ความไม่เปิดเผยตัวตน ออนไลน์ด้วยยิ่งยาก
      การพิสูจน์ความเป็นมนุษย์บางรูปแบบเป็นปัญหาที่แก้ยาก
    • สิ่งที่น่าทึ่งเกี่ยวกับ AI คือ ต่อให้คุณไม่ใช่คนเชียร์ AI สุดตัว คุณก็ยังสามารถลาก AI เข้ามาใส่ในบทสนทนาที่ไม่ได้เกี่ยวกับ AI ได้อยู่ดี ฝ่ายคัดค้านจะทำให้เอง
    • ที่แบบนั้นก็น่าจะเป็น lobsters นั่นแหละ หลังจากมีการถกเถียงขนาดใหญ่ อาจใหญ่ที่สุดในประวัติศาสตร์เว็บ โดยมีคอมเมนต์เกิน 300 ข้อความ ตอนนี้ก็ห้ามโพสต์ที่เป็น AI แล้ว
      กฎที่ทีมดูแลตั้งไว้จริง ๆ คือ “งานเขียนโดยมนุษย์อย่างมีนัยสำคัญ” แต่อย่าหลงไปกับถ้อยคำนี้ ใน lobsters มีคนจำนวนมากที่คัดค้าน LLM ในเชิงอุดมการณ์ เรื่องที่เทคโนโลยีถูกใช้อย่าง “มีนัยสำคัญ” แค่ไหนไม่ได้สำคัญมากนัก
      งานของผมถูกจัดว่าเป็นขยะเพียงเพราะ AI เคยแตะต้องมัน และก็มีคนเรียกผมว่าเป็นพวกชอบเปิดเผยตัวเองหรือพวกมีเฟติชเพียงเพราะผมบอกว่าใช้ AI เลยอยากเตือนไว้ล่วงหน้าสำหรับคนที่คิดจะสมัคร
    • น่าแดกดันที่คอมเมนต์นี้ซึ่งเป็นคอมเมนต์อันดับบนสุด ตอนนี้ก็กลายเป็นเรื่องเกี่ยวกับ AI ไปแล้ว
  • พอเจอประโยคอย่าง “การอนุญาตให้ unquote ฟังก์ชันลิเทอรัลทำให้ Janet สามารถเขียนแมโครที่โปร่งใสต่อการอ้างอิงได้อย่างสมบูรณ์” ก็รู้สึกว่าคนสาย Lisp นี่ตื่นเต้นกับเรื่องนามธรรมมากจริง ๆ
    ถ้าพูดแบบนี้กับคนทั่วไปบนถนน เขาคงวิ่งหนี

    • ตัวอย่างของแมโคร C ที่มีลิเทอรัลและ ไม่โปร่งใสต่อการอ้างอิง ก็เช่น
      #define MULTIPLY(x, y) x * y
      int result = MULTIPLY(2 + 3, 4); // 14
      การไม่รู้ว่าคำบางคำหมายถึงอะไรไม่ได้แปลว่าสิ่งนั้นแย่ จากถ้อยคำที่ใช้ ดูเหมือนเขาน่าจะหมายถึงอย่างนั้น
      การมีภาษาร่วมกันไว้พูดถึงรูปแบบและปัญหาที่เกิดซ้ำในงานเขียนโปรแกรมเป็นเรื่องดี การเอาศัพท์ไปล้อว่า “พวกโปรแกรมเมอร์กลุ่มนั้นประหลาดจริง” ไม่มีประโยชน์และมีแต่จะให้ผลย้อนกลับ
    • ชวนสงสัยว่าเคยลองอธิบาย ภาษาโปรแกรมเชิงวัตถุ และข้อดีของมันให้คนธรรมดาทั่วไปบนถนนฟังบ้างไหม
    • ราว ๆ หนึ่งปีก่อนผมเริ่มเขียนอินเทอร์พรีเตอร์ Scheme และไปได้ไกลพอสมควร แต่หยุดไปเมื่อไม่กี่เดือนก่อนตอนที่ได้งานใหม่
      ตอนนี้กำลังคิดว่าจะกลับไปทำต่อดีไหม และกำลังชั่งใจว่าฟีเจอร์เฉพาะทางเหล่านั้นคุ้มค่ากับการทำหรือเปล่า สำหรับผมมันก็ทำยากเหมือนกัน อาจจะข้าม dynamic-unwind ไป แล้วอาจตัด call/cc ทิ้งด้วย จากนั้นไปโฟกัสเรื่องการดีบักได้ ระบบนิเวศ ประสิทธิภาพ และการจัดการแพ็กเกจน่าจะดีกว่า
    • ทุกครั้งที่มีคนถามว่า “คุณทำงานอะไร” ผมก็มักจะได้ปฏิกิริยาแบบนั้น
      เลยพยายามตอบกว้าง ๆ ว่า “ทำงานเกี่ยวกับคอมพิวเตอร์” หรือไม่ก็บอกว่า “ไม่ใช่งานที่น่าสนใจเท่าไร” แล้วเปลี่ยนเรื่อง ถ้าลงรายละเอียดมากกว่านั้นอีกนิด คนก็เริ่มมองหาทางออกแล้ว
    • แม้แต่โปรแกรมเมอร์ทั่วไปก็น่าจะเป็นแบบนั้น
      พูดตามตรง ผมคิดว่าความที่คอมมูนิตี้สาย Lisp มีขนาดเล็กกลับเป็นข้อดีด้วยซ้ำ ยกตัวอย่างเช่น แม้แต่ Design Patterns รุ่นเก่ามากก็ยังเตือนให้เลือก composition มากกว่า inheritance แต่โปรแกรมเมอร์สาย OOP ก็ยังสร้างลำดับชั้นลึก 15 ชั้นกันอยู่ดี
  • ตอนที่ผมเพิ่งเริ่มรู้จัก Janet เอกสารพวกนี้ช่วยได้มากจริง ๆ
    https://janetdocs.org/tutorials
    https://janet.guide/ ทำโดยผู้เขียนโพสต์นี้

  • ผมเคยถูกดึงดูดทุกครั้งที่มีโพสต์เกี่ยวกับ Janet ขึ้นบน HN แต่ Janet for Mortals ที่ทุกคนชมกันมากนั้น กลับไม่รู้สึกว่าเป็นหนังสือสำหรับมนุษย์เดินดินเลยสักนิด

    • มีสื่อเริ่มต้นที่นุ่มนวลกว่านี้ด้วย: https://janetdocs.org/tutorials
    • แปลกดีนะ ภาษานี้ตรงไปตรงมาและเรียบง่ายมาก มีกฎให้จำอยู่น้อยมาก
      ถึงจะเป็น Lisp แต่พื้นผิวของภาษานั้นเล็กมาก เมื่อเทียบกับภาษาอื่น ๆ แล้ว Janet จัดว่าเรียนรู้ง่ายจริง ๆ เลยแปลกใจที่หนังสือเล่มนั้นยาก ผมไม่ได้อ่านหนังสือเล่มนั้น แต่คุ้นกับภาษานี้พอสมควร และพูดตามตรงก็มีแต่คำชม
    • สำหรับผมเป็นการส่วนตัว ไวยากรณ์ของแมโคร มาเร็วเกินไปในหนังสือจนทำให้สะดุด แต่หลังจากนั้นก็มีเนื้อหาที่มีคุณค่ามากจริง ๆ
    • ผมเคยรู้สึกแบบนั้นกับ Haskell Haskell ยากเกินไปสำหรับผม แต่ผมชอบไวยากรณ์ของมัน
      Janet ดูเหมือน Lisp 2.0 เพราะงั้นไวยากรณ์ก็เป็นสไตล์ Lisp
 
GN⁺ 2 시간 전
ความคิดเห็นจาก 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 เดิมได้

    • ตัวอย่างเจ๋ง ๆ ที่ทำด้วย Janet มี https://bauble.studio และมีบทความกระบวนการทำที่ https://ianthehenry.com/posts/bauble/building-bauble/ ด้วย การใช้ WASM สะดุดตามาก
      ยังมี 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” ในต้นฉบับน่าจะเป็นการใช้ถ้อยคำที่ไม่แม่นนัก สิ่งที่ตั้งใจสื่อใกล้เคียงกับ composite value type และ reference type มากกว่า
      คำว่า “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 เท่าไร ซึ่งไม่ค่อยถูกใจ

    • หนึ่งใน killer feature ของ Janet สำหรับผมคือการรองรับ PEG ในตัว ตอน ทำ textbox DSL สำหรับโปรเจกต์เกมงานอดิเรก มันสะดวกมาก
      เป็นฟีเจอร์ที่พอไป parse ข้อความในภาษาอื่นเมื่อไรก็จะคิดถึงทันที
    • การ destructure แบบนั้นไม่ใช่รูปแบบที่ใช้กันบ่อย และ array/tuple ของ Janet ก็ไม่ใช่ linked list
      Janet ไม่ได้ใกล้กับ Clojure ฉบับเล็กที่ embed ได้มากนัก แต่ใกล้กับ Lua ที่รองรับ functional programming ได้ดีกว่า มากกว่า