3 คะแนน โดย GN⁺ 23 일 전 | 1 ความคิดเห็น | แชร์ทาง WhatsApp
  • เป็นภาษาขนาดเล็กที่ทำงานบน รันไทม์ 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
  • เป็นภาษาเชิงนิพจน์ โดย 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 ความคิดเห็น

 
GN⁺ 23 일 전
ความคิดเห็นจาก 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

    • Go ยังคงโดดเด่นไม่เหมือนใครในแง่ที่มี concurrency runtime แบบอิง GC
      ในปัญหาที่ต้องจัดการ reference graph ซับซ้อน GC เป็นสิ่งจำเป็น และด้วยโครงสร้าง user-mode stack ของ Go จึงมี memory model ที่มีประสิทธิภาพ
    • ในภาษาแบบ GC ความเร็วในการพัฒนาสูงกว่ามาก ผมเคยทำแชตบอตด้วย Rust และ Python แม้จะมีประสบการณ์ Rust อยู่แล้ว แต่ Python ก็เร็วกว่าเยอะ
      Go ก็เหมาะกับการทำ เครื่องมือ CLI แบบรวดเร็ว ลักษณะนี้ด้วย — เช่น wordle-tui
    • Go อาจมีความแปลกหลายอย่างในฐานะภาษา แต่ยอดเยี่ยมมากในฐานะ เป้าหมายการคอมไพล์
      ไวยากรง่าย รองรับครอสแพลตฟอร์ม มี runtime และ GC ในตัว โครงสร้าง “errors as values”, green thread, คอมไพเลอร์ AOT ที่เร็ว ฯลฯ ล้วนเป็นข้อดี
      defer ของ Go มีประโยชน์ แต่ การจัดการ error และกฎเรื่อง scope ยังดูแปลก
    • วลีที่บล็อกพูดไว้ว่า “Go เป็นภาษาที่พลาดเรื่อง NULL สองครั้ง” น่าประทับใจมาก
      TypeScript ก็แก้ปัญหานี้ไม่ได้ แถมแย่กว่าเดิมอีก เลยทำ แพ็กเกจ Option type ขึ้นมาเองแล้วปล่อยบน NPM → fp-sdk
    • async ของ Rust สะดวกน้อยกว่า Go เพราะไม่มี GC แค่ข้อนี้ก็เป็นเหตุผลให้เลือก Go runtime ได้แล้ว
  • มีหลายภาษาที่คอมไพล์เป็น Go อยู่แล้ว — XGo, Borgo, Soppo เป็นต้น

    • Borgo กับ Lisette แค่แทนค่าการคืนแบบ (T, error) ให้เป็น Result type อย่างตรงไปตรงมา แต่ในเชิงความหมายแล้วมันไม่เหมือนกันทั้งหมด
      ตัวอย่างเช่น io.Reader.Read นั้น (n!=0, io.EOF) หมายถึงการจบแบบปกติ ดังนั้นถ้าจัดการเป็น error ตรง ๆ จะทำงานผิดพลาดได้
    • สงสัยว่า compile 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 ได้

    • ความต่างด้านไวยากรณ์แบบนี้เป็นเรื่องเล็กน้อย แก่นสำคัญคือการ นำ type system ของ Rust มาใส่ใน Go
      int กับ float64 ก็ตั้งชื่อตามธรรมเนียม type naming ของ Go
    • เวลาใช้หลายภาษาไปมา ความคล้ายกันของไวยากรณ์กลับยิ่ง สร้างความสับสน ได้ เช่นผมมักลืมว่าใน PHP ต้องใช้ . แทน +
    • ตอนแรกผมเองก็เคยคิดจะทำภาษาสไตล์ Rust บน TypeScript แต่สุดท้ายก็ตระหนักว่า ตัว Rust เองไม่ได้ยากอย่างที่คิด
    • การทำ memory model แบบ Rust ในภาษา GC นั้น ไม่เป็นธรรมชาติอย่างมาก มันเหมือนแต่ละอ็อบเจ็กต์มี address space แยกกันเอง ทำให้ซับซ้อน
    • Lisette เป็นภาษาที่ ได้แรงบันดาลใจจาก Rust ไม่ได้พยายามจะเป็น Rust และตั้งเป้าหมายหลักไปที่ นักพัฒนา Go
  • Go runtime ดีมาก แต่ตัวภาษาเอง ดูหยาบและเหมือนไม่มีความตั้งใจจะปรับปรุง
    ดังนั้นถ้าถึงขั้นจะใช้ transpiler ก็คงต้องไม่ชอบ Go มากจริง ๆ

  • สงสัยว่าทำไม Rust หรือภาษาตระกูล Rust ถึง แยก struct กับ method ออกจากกัน
    ทำไมถึงนิยามเมธอดไว้ใน struct โดยตรงไม่ได้

    • ใน Rust ฟิลด์ของ struct มีผลต่อ auto-trait จึงสำคัญที่จะมองเห็นฟิลด์ทั้งหมดได้ในทีเดียว
      อีกทั้ง impl block ยังสามารถมี generic constraint ที่ต่างจาก struct ได้ จึงนิยามได้หลายชุด
      และท้ายที่สุด Rust เป็นภาษาที่ออกแบบมาให้คิดโดยยึด รูปร่างของข้อมูล (Shape) เป็นศูนย์กลาง
    • ส่วนตัวผมรู้สึกว่า impl block ก็คล้ายเมธอดของ Go และไม่ได้มองว่าฝั่งไหนดีกว่าอีกฝั่งอย่างชัดเจน
  • ถ้ามีภาษาที่หน้าตาเหมือน Python แต่คอมไพล์เป็น Rust หรือ Go ได้ก็คงยอดเยี่ยมมาก

    • Mojo คือ ภาษาสมรรถนะสูงบนไวยากรณ์ Python ที่สร้างโดยผู้สร้าง Swift
    • Spy เป็นความพยายามระยะแรกที่คอมไพล์เป็น C และ Nim ก็เป็นภาษาที่โตเต็มที่ในสายคล้ายกัน
    • Nim มีไวยากรณ์คล้าย Python พร้อม static type system และคอมไพล์ได้หลายเป้าหมาย เช่น wasm และ C
    • Static Python Skill เป็นความพยายามที่จะคอมไพล์ Python แบบสแตติก
    • Grumpy เคยเป็น transpiler จาก Python→Go ที่ Google ทำ แต่ ไม่มีอัปเดตมา 9 ปีแล้ว (รองรับ Python 2.7)
  • 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 นั้นน่าดึงดูดมาก
    ผมจะคอยติดตามพัฒนาการของโปรเจ็กต์นี้ต่อไป