3 điểm bởi GN⁺ 17 ngày trước | 1 bình luận | Chia sẻ qua WhatsApp
  • Là một ngôn ngữ nhỏ chạy trên runtime Go trong khi sử dụng cú pháp kiểu Rust, kết hợp ưu điểm của cả hai ngôn ngữ
  • Cấu trúc tăng cường độ an toàn và khả năng biểu đạt với kiểu dữ liệu đại số, pattern matching, hệ thống kiểu Hindley-Milner, tính bất biến mặc định
  • Đảm bảo khả năng tương tác với hệ sinh thái Go thông qua import trực tiếp package Go, toán tử pipeline, khối try, đồng thời dựa trên task
  • Cải thiện trải nghiệm lập trình viên và độ ổn định của mã với phát hiện lỗi khi biên dịch, thông báo chẩn đoán rõ ràng, hỗ trợ LSP
  • Điểm cốt lõi là mã Lisette được chuyển thành mã Go rõ ràng, dễ đọc, nên có thể tích hợp tự nhiên với các dự án Go hiện có

Tổng quan về Lisette

  • Lisette là một ngôn ngữ nhỏ dựa trên cú pháp Rust và được biên dịch sang runtime Go
  • Có các đặc trưng như kiểu dữ liệu đại số, pattern matching, không có nil, hệ thống kiểu Hindley-Milner, tính bất biến mặc định, khả năng tương tác với hệ sinh thái Go
  • Có thể cài đặt bằng lệnh cargo install lisette, và có thể import trực tiếp các package như fmt, io, os của Go để sử dụng

Cú pháp quen thuộc

  • cấu trúc cú pháp tương tự Rust
    • Hỗ trợ pattern matching thông qua enummatch
    • Có thể định nghĩa phương thức bằng struct và khối impl
  • ngôn ngữ hướng biểu thức, trong đó if, let, các block đều trả về giá trị
  • Hỗ trợ chaining và lambda, giúp biểu đạt ngắn gọn việc xử lý biến môi trường hoặc thao tác chuỗi
  • Hỗ trợ interface và generic, có thể viết hàm generic thông qua định nghĩa interface và ràng buộc T: Trait
  • Có thể xử lý ngắn gọn kiểu Option bằng cú pháp if letlet else

Tính an toàn

  • Phát hiện ở thời điểm biên dịch các lỗi có thể xảy ra trên runtime Go

    • Nếu không xử lý hết mọi pattern trong câu lệnh match thì sẽ phát sinh lỗi
    • Không cho phép dùng nil, biểu diễn giá trị thiếu bằng Option<T>
    • Cảnh báo nếu bỏ qua giá trị trả về Result
    • Cảnh báo khi làm lộ kiểu không công khai trong API công khai
    • Lỗi khi truyền biến bất biến làm đối số có thể thay đổi
    • Lỗi biên dịch khi thiếu field của struct
    • Thông báo chẩn đoán cung cấp vị trí mã cụ thể và gợi ý cách sửa
    • Hỗ trợ LSP (Language Server Protocol) nên có thể dùng trong các editor phổ biến như VSCode, Neovim, Zed

Tính tiện dụng

  • Được thiết kế với trọng tâm là khả năng tương tác với Go
  • Dùng toán tử pipeline (|>) để biểu đạt function chaining ngắn gọn
  • Đơn giản hóa việc lan truyền lỗi thông qua khối try
  • Đồng thời được triển khai tương tự goroutine của Go bằng taskChannel
  • Có thể chỉ định tên field JSON, bỏ qua field, chuyển đổi sang chuỗi, tag kiểm chứng... bằng attribute tuần tự hóa
  • Cung cấp khối recover để phục hồi panic, và có thể xử lý lỗi an toàn bằng kiểu Result
  • Hỗ trợ cú pháp defer để đảm bảo dọn dẹp tài nguyên hoặc rollback transaction

Kết quả biên dịch minh bạch

  • Mã Lisette được chuyển thành mã Go rõ ràng và dễ đọc
    • Kiểu OptionResult lần lượt được chuyển thành struct lisette.Optionlisette.Result
    • Cú pháp match được chuyển thành câu lệnh điều kiện của Go để xử lý từng nhánh
    • Toán tử ? được thay thế nội bộ bằng mã kiểm tra Result
  • Ví dụ, hàm classify nhận Option<int> rồi được chuyển thành các câu lệnh điều kiện tường minh của Go; hàm combine thì được chuyển thành mã Go kiểm tra Result

Thông tin thêm

  • Kho lưu trữ chính thức: github.com/ivov/lisette
  • Được phát hành theo giấy phép MIT, và tính đến năm 2026 do Iván Ovejero phát triển

1 bình luận

 
Ý kiến Hacker News
  • Tôi đã trò chuyện với tác giả và dù chưa trực tiếp dùng ngôn ngữ này, Lisette có vẻ khá thú vị và rõ ràng là một cải tiến so với Go
    Tuy vậy, tôi nghĩ sẽ khó có thể vượt qua hoàn toàn các giới hạn của Go. Ví dụ, vấn đề typed nil xuất phát từ kiểu interface của Go được xử lý bằng Option trong Lisette, nhưng việc unwrapping hai lớp (Some(Some(h))) có thể trở nên gượng gạo
    Ngoài ra, cách dùng defer của Go vẫn còn bất tiện và không có cơ chế giải phóng tài nguyên tự động kiểu RAII
    TypeScript bổ sung cho JavaScript vì không có lựa chọn thay thế nào chạy được trên trình duyệt, nhưng giờ đã có WASM nên tình hình đã khác
    Vì vậy tôi tự hỏi: “Đã có Rust rồi thì tại sao còn muốn biến Go thành Rust?” Dù vậy, có vẻ Lisette đang nhắm tới điểm ở giữa
    Cuối cùng, Lisette có vẻ phù hợp với những ai muốn cải thiện codebase Go hiện có hoặc vẫn muốn tiếp tục dùng runtime Go
    Điều tôi thắc mắc là thiếu một hướng dẫn bắt đầu nhanh trả lời câu hỏi: “Nếu muốn viết file sau bằng Lisette thay vì Go thì phải bắt đầu như thế nào?”
    Bài blog liên quan: Go is still not good

    • Go vẫn là thứ gần như không có đối thủ ở chỗ nó cung cấp runtime đồng thời dựa trên GC
      Trong những miền bài toán phải xử lý đồ thị tham chiếu phức tạp, GC là thứ thiết yếu, và nhờ cấu trúc stack ở user mode nên Go có mô hình bộ nhớ hiệu quả
    • Với ngôn ngữ GC, tốc độ phát triển nhanh hơn rất nhiều. Tôi từng làm chatbot bằng Rust và Python, và dù có kinh nghiệm Rust thì Python vẫn nhanh hơn hẳn
      Go cũng phù hợp để làm các công cụ CLI nhanh kiểu này — ví dụ: wordle-tui
    • Go có nhiều điểm kỳ quặc nếu xét như một ngôn ngữ, nhưng lại rất tuyệt nếu xét như một mục tiêu biên dịch
      Cú pháp đơn giản, hỗ trợ đa nền tảng, runtime và GC tích hợp sẵn, mô hình “errors as values”, green thread, trình biên dịch AOT nhanh... đều là các ưu điểm lớn
      defer của Go hữu ích, nhưng xử lý lỗi và quy tắc scope vẫn khá gượng
    • Câu trong bài blog nói rằng “Go là ngôn ngữ đã lỡ tạo ra NULL đến hai lần” rất ấn tượng
      TypeScript cũng không giải quyết được vấn đề này, thậm chí còn tệ hơn. Vì vậy tôi đã tự viết một gói kiểu Option rồi đưa lên NPM → fp-sdk
    • async của Rust kém tiện hơn Go vì không có GC. Chỉ riêng điểm này cũng đã là lý do để chọn runtime Go
  • Đã có khá nhiều ngôn ngữ biên dịch sang Go — như XGo, Borgo, Soppo

    • Borgo và Lisette chỉ đơn giản thay (T, error) bằng kiểu Result, nhưng về mặt ngữ nghĩa thì không hoàn toàn giống nhau
      Ví dụ io.Reader.Read dùng (n!=0, io.EOF) để biểu thị kết thúc bình thường, nên nếu chỉ coi đó là lỗi thì sẽ dẫn đến hành vi sai
    • Tôi tò mò không biết lỗi biên dịch được truyền từ ngôn ngữ đích về ngôn ngữ nguồn như thế nào
  • Chất lượng thông báo lỗi của Lisette rất ấn tượng. Các gợi ý “help” thực sự hữu ích
    Tuy nhiên, tôi lo rằng mã sau khi chuyển sang Go có thể trở nên dài dòng, nên khi gặp lỗi runtime thì sẽ phải debug trong mã Go
    Ngoài ra, chiều gọi Lisette từ code Go hiện có cũng có vẻ khó
    Tôi cũng muốn biết Lisette là một ngôn ngữ thử nghiệm hay đang hướng tới sản xuất thực tế

    • Chính tác giả đã trả lời: nếu dùng tùy chọn lis run --debug thì trong mã Go sẽ được chèn chú thích //line source.lis:21:5 để stack trace được ánh xạ về mã Lisette gốc
      LSP xử lý lỗi compile-time dựa trên file .lis
      Hiện chưa có tính năng gọi Lisette từ Go, nhưng ưu tiên trước mắt là import package Go từ Lisette
      Ban đầu đây là một thử nghiệm, nhưng mục tiêu là phát triển nó thành ngôn ngữ ở mức production
  • Tôi thắc mắc vì sao không mang nguyên cú pháp giống Rust vào luôn
    Ví dụ: dùng use foo::bar thay cho import "foo.bar", hay Bar::Baz => thay cho Bar.Baz =>
    Người biết Rust sẽ thấy rối, còn người không biết Rust thì cũng không chuyển giao được kiến thức sang Rust

    • Những khác biệt cú pháp như vậy là chuyện nhỏ; cốt lõi là đưa hệ thống kiểu của Rust vào Go
      intfloat64 tuân theo quy ước đặt tên kiểu của Go
    • Khi chuyển qua lại giữa nhiều ngôn ngữ, sự giống nhau về cú pháp đôi khi lại gây nhầm lẫn. Ví dụ tôi hay quên rằng trong PHP phải dùng . thay vì +
    • Ban đầu tôi cũng từng định làm một ngôn ngữ kiểu Rust dựa trên TypeScript, nhưng rồi nhận ra rằng bản thân Rust thật ra không khó như tưởng tượng
    • Việc triển khai mô hình bộ nhớ kiểu Rust trong một ngôn ngữ GC là điều rất không tự nhiên. Nó giống như mỗi object có một không gian địa chỉ riêng, nên rất phức tạp
    • Lisette là ngôn ngữ lấy cảm hứng từ Rust, chứ không phải cố trở thành Rust. Nhà phát triển Go mới là đối tượng mục tiêu chính
  • Runtime của Go thì tốt, nhưng bản thân ngôn ngữ lại thô và có vẻ không có ý định cải thiện
    Vì thế nếu đã phải dùng transpiler thì chắc hẳn là phải ghét Go lắm

  • Tôi thắc mắc vì sao Rust hay các ngôn ngữ họ Rust lại tách struct và method ra
    Tại sao không thể định nghĩa method trực tiếp bên trong struct?

    • Trong Rust, các field của struct ảnh hưởng đến auto-trait, nên việc nhìn toàn bộ field trong một lần là rất quan trọng
      Ngoài ra, khối impl có thể có ràng buộc generic khác với struct, nên có thể định nghĩa nhiều impl khác nhau
      Cuối cùng, Rust là ngôn ngữ được thiết kế để người dùng tư duy xoay quanh hình dạng dữ liệu (Shape)
    • Cá nhân tôi thấy khối impl khá giống method của Go, và khó nói cách nào tốt hơn
  • Nếu có một ngôn ngữ trông như Python nhưng biên dịch sang Rust hoặc Go thì sẽ rất tuyệt

    • Mojongôn ngữ hiệu năng cao với cú pháp dựa trên Python do người tạo ra Swift làm ra
    • Spy là một nỗ lực ban đầu biên dịch sang C, còn Nim là một ngôn ngữ trưởng thành hơn cùng nhóm này
    • Nim có cú pháp giống Python cùng với hệ thống kiểu tĩnh, và biên dịch được sang nhiều đích như wasm và C
    • Static Python Skill là một nỗ lực nhằm biên dịch Python theo hướng tĩnh
    • Grumpy từng là transpiler Python→Go do Google tạo ra, nhưng đã 9 năm không được cập nhật (nhắm tới Python 2.7)
  • Lisette có vẻ là ngôn ngữ cân bằng tốt giữa sự đơn giản của Go và sự phức tạp của Rust
    Tôi tò mò không biết có lý do gì khiến tốc độ biên dịch của nó chậm hơn Go nhiều hay không, và có những phần nào của Rust đã được chủ đích loại bỏ
    Ví dụ: borrow checking, kiểu dữ liệu, async...

  • Go là ngôn ngữ dễ học nhưng thiếu tính năng
    Lisette trông như một ngôn ngữ lấp đầy những khoảng trống đó, nên khá thú vị
    Nếu giống như TypeScript mở rộng JavaScript, việc bổ sung cho Go một hệ thống kiểu giàu khả năng biểu đạt và một trình biên dịch nghiêm ngặt có thể tạo ra một ngôn ngữ backend rất tốt
    Gợi ý cá nhân của tôi là hỗ trợ chia sẻ kiểu với frontend TypeScript. Đây cũng là một trong những lý do TypeScript được ưa chuộng ở backend

  • Với tư cách là một lập trình viên tự động hóa hạ tầng đang phân vân giữa độ an toàn của Rust và sự đơn giản của Go, ý tưởng mang tính dễ dùng của Rust đặt lên trên runtime Go là cực kỳ hấp dẫn
    Tôi sẽ tiếp tục theo dõi sự phát triển của dự án này