- เป็นภาษาขนาดเล็กที่ทำงานบน รันไทม์ Go พร้อมใช้ไวยากรณ์สไตล์ Rust จึงเป็นรูปแบบที่ผสานข้อดีของทั้งสองภาษาเข้าด้วยกัน
- โครงสร้างที่เสริมทั้งความปลอดภัยและพลังในการแสดงออกด้วย algebraic data types, pattern matching, ระบบชนิด Hindley-Milner, และ immutable โดยปริยาย
- รองรับการ import แพ็กเกจ Go โดยตรง, pipeline operator, try block, และ concurrency แบบอิง task เพื่อให้ทำงานร่วมกับระบบนิเวศ Goได้
- ยกระดับประสบการณ์นักพัฒนาและความเสถียรของโค้ดด้วย การตรวจจับข้อผิดพลาดตั้งแต่คอมไพล์ไทม์, ข้อความวินิจฉัยที่ชัดเจน, และ การรองรับ LSP
- จุดสำคัญคือ โค้ด Lisette ถูกแปลงเป็นโค้ด Go ที่อ่านง่าย จึงผสานเข้ากับโปรเจ็กต์ Go เดิมได้อย่างเป็นธรรมชาติ
ภาพรวมของ Lisette
- Lisette เป็นภาษาขนาดเล็กที่อิงไวยากรณ์ Rust และคอมไพล์สู่รันไทม์ Go
- มีจุดเด่นคือ algebraic data types, pattern matching, ไม่มี nil, ระบบชนิด Hindley-Milner, immutable โดยปริยาย, และ การทำงานร่วมกับระบบนิเวศ Go
- ติดตั้งได้ด้วยคำสั่ง
cargo install lisetteและสามารถ import แพ็กเกจของ Go อย่างfmt,io,osมาใช้ได้โดยตรง
ไวยากรณ์ที่คุ้นเคย
- มีโครงสร้างไวยากรณ์คล้าย Rust
- รองรับpattern matching ผ่าน
enumและmatch - สามารถนิยามเมธอดได้ด้วยบล็อก
structและimpl
- รองรับpattern matching ผ่าน
- เป็นภาษาเชิงนิพจน์ โดย
if,letและบล็อกต่าง ๆ ล้วนคืนค่าได้ - รองรับchaining และ lambda ทำให้เขียนการจัดการตัวแปรสภาพแวดล้อมหรือการจัดการสตริงได้อย่างกระชับ
- รองรับinterface และ generic และสามารถเขียนฟังก์ชัน generic ด้วยการนิยาม
interfaceและข้อกำหนดT: Trait - รองรับไวยากรณ์ if let และ let else เพื่อจัดการชนิด
Optionได้อย่างกระชับ
ความปลอดภัย
-
ตรวจจับข้อผิดพลาดที่อาจเกิดขึ้นบนรันไทม์ Go ได้ตั้งแต่คอมไพล์ไทม์
- หากในคำสั่ง
matchจัดการแพตเทิร์นไม่ครบทั้งหมด จะเกิดข้อผิดพลาด - ไม่อนุญาตให้ใช้
nilและใช้ Option<T> เพื่อแทนค่าที่หายไป - หากละเลยค่าที่คืนจาก Result จะมีคำเตือน
- มีคำเตือนเมื่อเปิดเผยชนิดที่ไม่เป็นสาธารณะผ่าน public API
- หากส่งตัวแปร immutable เป็นอาร์กิวเมนต์แบบเปลี่ยนค่าได้ จะเกิดข้อผิดพลาด
- หากขาดฟิลด์ของ struct จะเกิดข้อผิดพลาดตอนคอมไพล์
- ข้อความวินิจฉัยจะแสดงทั้งตำแหน่งโค้ดที่ชัดเจนและคำแนะนำในการแก้ไข
- รองรับ LSP(Language Server Protocol) จึงใช้งานได้กับเอดิเตอร์หลักอย่าง VSCode, Neovim, Zed เป็นต้น
- หากในคำสั่ง
การใช้งาน
- ออกแบบโดยเน้นการทำงานร่วมกับ Goเป็นหลัก
- ใช้ pipeline operator(
|>) เพื่อเขียน function chaining ได้อย่างกระชับ - ใช้ try block เพื่อทำให้การส่งต่อข้อผิดพลาดง่ายขึ้น
- ด้านconcurrency ใช้
taskและChannelเพื่อทำงานคล้าย goroutine ของ Go - สามารถกำหนดชื่อฟิลด์ JSON, การละเว้น, การแปลงเป็นสตริง, และแท็กสำหรับการตรวจสอบได้ผ่าน serialization attribute
- มีบล็อก
recoverสำหรับกู้คืนจาก panic และรองรับการจัดการข้อผิดพลาดอย่างปลอดภัยผ่านชนิดResult - รองรับคำสั่ง
deferเพื่อรับประกันการล้างทรัพยากรหรือการ rollback ธุรกรรม
ผลลัพธ์การคอมไพล์ที่โปร่งใส
- โค้ด Lisette จะถูกแปลงเป็นโค้ด Go ที่ชัดเจนและอ่านง่าย
- ชนิด
OptionและResultจะถูกแปลงเป็นโครงสร้างlisette.Optionและlisette.Resultตามลำดับ - คำสั่ง
matchจะถูกแปลงเป็นคำสั่งเงื่อนไขของ Go เพื่อจัดการแต่ละสาขา - ตัวดำเนินการ
?จะถูกแทนภายในด้วยโค้ดตรวจสอบResult
- ชนิด
- ตัวอย่างเช่น ฟังก์ชัน
classifyรับOption<int>และถูกแปลงเป็นคำสั่งเงื่อนไขแบบชัดเจนของ Go ขณะที่ฟังก์ชันcombineจะถูกแปลงเป็นโค้ด Go ที่ตรวจสอบResult
ข้อมูลเพิ่มเติม
- ที่เก็บอย่างเป็นทางการ: github.com/ivov/lisette
- เผยแพร่ภายใต้ MIT License และ ณ ปี 2026 พัฒนาโดย Iván Ovejero
1 ความคิดเห็น
ความคิดเห็นจาก Hacker News
ได้คุยกับผู้เขียนแล้ว และแม้ยังไม่ได้ลองใช้ภาษานี้เอง แต่ Lisette ดูเป็นภาษาที่น่าสนใจและชัดเจนว่าปรับปรุงจาก Go ขึ้นมา
แต่ก็คิดว่ายังยากที่จะก้าวข้ามข้อจำกัดของ Go ได้ทั้งหมด ตัวอย่างเช่น ปัญหา
typed nilที่มาจาก interface type ของ Go ใน Lisette จัดการด้วย Option ได้ แต่การ unwrapping ซ้อน (Some(Some(h))) อาจดูแปลกอยู่บ้างอีกทั้งแนวทาง defer ของ Go ก็ยังไม่สะดวก และไม่ได้มีการคืนทรัพยากรอัตโนมัติแบบ RAII
ที่ TypeScript เข้ามาเติมเต็ม JavaScript ได้ก็เพราะไม่มีทางเลือกอื่นที่รันบนเบราว์เซอร์ได้ แต่ตอนนี้มี WASM แล้ว สถานการณ์จึงต่างออกไป
เลยทำให้นึกสงสัยว่า “ในเมื่อมี Rust อยู่แล้ว ทำไมต้องทำ Go ให้เหมือน Rust?” แต่ Lisette ก็ดูเหมือนจะเล็งจุดกึ่งกลางระหว่างสองฝั่งนี้
สุดท้ายแล้ว Lisette น่าจะเหมาะกับคนที่อยากปรับปรุง codebase Go เดิม หรือยังอยากใช้ Go runtime ต่อไป
สิ่งที่ผมสงสัยคือยังไม่มี คู่มือเริ่มต้นแบบเร็ว ที่ตอบคำถามว่า “ถ้าจะเขียนไฟล์ต่อไปนี้ด้วย Lisette แทน Go ต้องเริ่มยังไง”
บทความบล็อกที่เกี่ยวข้อง: Go is still not good
ในปัญหาที่ต้องจัดการ reference graph ซับซ้อน GC เป็นสิ่งจำเป็น และด้วยโครงสร้าง user-mode stack ของ Go จึงมี memory model ที่มีประสิทธิภาพ
Go ก็เหมาะกับการทำ เครื่องมือ CLI แบบรวดเร็ว ลักษณะนี้ด้วย — เช่น wordle-tui
ไวยากรง่าย รองรับครอสแพลตฟอร์ม มี runtime และ GC ในตัว โครงสร้าง “errors as values”, green thread, คอมไพเลอร์ AOT ที่เร็ว ฯลฯ ล้วนเป็นข้อดี
deferของ Go มีประโยชน์ แต่ การจัดการ error และกฎเรื่อง scope ยังดูแปลกTypeScript ก็แก้ปัญหานี้ไม่ได้ แถมแย่กว่าเดิมอีก เลยทำ แพ็กเกจ Option type ขึ้นมาเองแล้วปล่อยบน NPM → fp-sdk
มีหลายภาษาที่คอมไพล์เป็น Go อยู่แล้ว — XGo, Borgo, Soppo เป็นต้น
(T, error)ให้เป็น Result type อย่างตรงไปตรงมา แต่ในเชิงความหมายแล้วมันไม่เหมือนกันทั้งหมดตัวอย่างเช่น
io.Reader.Readนั้น(n!=0, io.EOF)หมายถึงการจบแบบปกติ ดังนั้นถ้าจัดการเป็น error ตรง ๆ จะทำงานผิดพลาดได้คุณภาพของข้อความ error ใน Lisette น่าประทับใจมาก คำใบ้ “help” ดูมีประโยชน์จริง
แต่ก็เป็นห่วงว่าโค้ดที่แปลงเป็น Go อาจยืดยาว ทำให้เวลาเกิด runtime error ต้องไปดีบักในโค้ด Go
อีกทั้งการเรียก Lisette จากโค้ด Go เดิมก็ดูยาก
เลยสงสัยว่า Lisette เป็นภาษาเชิงทดลอง หรือมุ่งเป้าไปที่งาน production จริง
lis run --debugจะมีการแทรกคอมเมนต์//line source.lis:21:5ลงในโค้ด Go ทำให้ stack trace ถูกแมปกลับไปยังโค้ด Lisette ต้นฉบับLSP จะจัดการ compile-time error โดยอิงจากไฟล์
.lisตอนนี้ยังไม่มีฟังก์ชันให้ Go เรียก Lisette ได้ แต่ความสำคัญลำดับแรกคือ ให้ Lisette import แพ็กเกจ Go ได้
ตอนแรกเริ่มจากการทดลอง แต่เป้าหมายคือพัฒนาไปเป็น ภาษาระดับ production
สงสัยว่าทำไมถึงไม่ยกไวยากรณ์คล้าย Rust มาใช้ตรง ๆ
เช่น
import "foo.bar"แทนที่จะเป็นuse foo::bar, หรือBar.Baz =>แทนBar::Baz =>คนที่รู้ Rust จะสับสน ส่วนคนที่ไม่รู้ Rust ก็ไม่สามารถถ่ายโอนความรู้ไปใช้กับ Rust ได้
intกับfloat64ก็ตั้งชื่อตามธรรมเนียม type naming ของ Go.แทน+Go runtime ดีมาก แต่ตัวภาษาเอง ดูหยาบและเหมือนไม่มีความตั้งใจจะปรับปรุง
ดังนั้นถ้าถึงขั้นจะใช้ transpiler ก็คงต้องไม่ชอบ Go มากจริง ๆ
สงสัยว่าทำไม Rust หรือภาษาตระกูล Rust ถึง แยก struct กับ method ออกจากกัน
ทำไมถึงนิยามเมธอดไว้ใน struct โดยตรงไม่ได้
อีกทั้ง impl block ยังสามารถมี generic constraint ที่ต่างจาก struct ได้ จึงนิยามได้หลายชุด
และท้ายที่สุด Rust เป็นภาษาที่ออกแบบมาให้คิดโดยยึด รูปร่างของข้อมูล (Shape) เป็นศูนย์กลาง
ถ้ามีภาษาที่หน้าตาเหมือน Python แต่คอมไพล์เป็น Rust หรือ Go ได้ก็คงยอดเยี่ยมมาก
Lisette ดูเป็นภาษาที่ รักษาสมดุลระหว่างความเรียบง่ายของ Go กับความซับซ้อนของ Rust ได้ดี
เลยสงสัยว่ามีเหตุผลอะไรที่ทำให้ความเร็วคอมไพล์ช้ากว่า Go มากไหม และมีฟีเจอร์ไหนของ Rust ที่ตั้งใจตัดออกไปบ้าง
เช่น borrow checking, data type, async เป็นต้น
Go เป็นภาษาที่เรียนง่าย แต่ขาดฟีเจอร์
Lisette ดูเป็นภาษาที่มา เติมช่องว่างนั้น จึงน่าสนใจ
เหมือนที่ TypeScript ขยาย JavaScript ถ้าเพิ่ม type system ที่แสดงออกได้มากขึ้น และ คอมไพเลอร์ที่เข้มงวด ให้ Go มันก็น่าจะกลายเป็นภาษา backend ที่ยอดเยี่ยมได้
ข้อเสนอส่วนตัวของผมคือรองรับการ แชร์ type กับฟรอนต์เอนด์ TypeScript เพราะนี่เป็นหนึ่งในเหตุผลที่ TypeScript ได้รับความนิยมในฝั่ง backend ด้วย
ในฐานะนักพัฒนา automation infrastructure ที่ชั่งใจระหว่างความปลอดภัยของ Rust กับความเรียบง่ายของ Go ไอเดียที่จะเอา การใช้งานแบบ Rust มาวางบน Go runtime นั้นน่าดึงดูดมาก
ผมจะคอยติดตามพัฒนาการของโปรเจ็กต์นี้ต่อไป