Gleam v1.19.0: Không còn biên dịch sang mã nguồn Erlang

Công nghệ06 tháng 10, 2026·10 phút đọc

Gleam v1.19.0 đánh dấu bước ngoặt lớn khi trình biên dịch chuyển từ tạo mã nguồn Erlang sang Erlang abstract forms, giúp tăng tốc độ build đáng kể và cải thiện độ chính xác của thông tin gỡ lỗi. Bản phát hành cũng mang đến nhiều tối ưu cho JavaScript, hỗ trợ TypeScript tốt hơn và cải thiện thông báo lỗi.

Gleam v1.19.0: Không còn biên dịch sang mã nguồn Erlang

Gleam v1.19.0: Không còn biên dịch sang mã nguồn Erlang

Gleam là ngôn ngữ lập trình an toàn kiểu (type-safe) và có khả năng mở rộng, chạy trên máy ảo Erlang (BEAM) và cả runtime JavaScript. Phiên bản v1.19.0 vừa được phát hành với một thay đổi mang tính bước ngoặt trong kiến trúc trình biên dịch, cùng nhiều cải tiến đáng chú ý khác.

Minh họa cộng đồng lập trình viênMinh họa cộng đồng lập trình viên

Mục tiêu biên dịch hoàn toàn mới

Trong vài tháng qua, Giacomo Cavalieri đã viết lại hoàn toàn bộ sinh mã Erlang (Erlang code generator) của Gleam với thiết kế khác biệt và đặc biệt là tạo ra định dạng đầu ra mới. Trước đây Gleam sinh ra mã nguồn Erlang, còn nay nó sinh ra Erlang abstract forms.

Erlang abstract forms là một dạng biểu diễn trung gian (intermediate representation) mà trình biên dịch Erlang sử dụng. Đây là một cây có chú thích metadata, biểu diễn cú pháp Erlang, thường được tạo ra bằng cách chạy bộ tokenizer và parser của Erlang. Định dạng này có mã hóa nhị phân sử dụng external term format của Erlang, và nhờ đó Gleam có thể nạp trực tiếp mã đã sinh, bỏ qua nửa đầu của trình biên dịch Erlang.

Việc chuyển sang Erlang abstract forms mang lại nhiều lợi ích:

  • Hiệu năng trình biên dịch được cải thiện, giúp giảm đáng kể thời gian build cho các dự án Gleam chạy trên Erlang.
  • Metadata vị trí chính xác hơn: thông tin vị trí mã nguồn mà runtime nhận được nay khớp với mã nguồn Gleam gốc, thay vì mã Erlang do trình biên dịch sinh ra. Điều này có nghĩa là số dòng trong báo cáo lỗi và stacktrace của BEAM chính xác tuyệt đối, trong khi trước đây có thể chỉ trỏ tới hàm gần nhất. Metadata này cũng có thể mở đường cho hỗ trợ đầy đủ Gleam trong các trình gỡ lỗi như edb.
  • Chất lượng mã nguồn của trình biên dịch Gleam được nâng cao. Bộ sinh mã Erlang trước đây là một trong những phần cũ và ổn định nhất của Gleam, nhưng chưa tuân theo các chuẩn và quy ước hiện hành. Phiên bản thay thế mới được đánh giá là nâng tầm toàn bộ trình biên dịch.

Nhanh đến mức nào?

Cần lưu ý rằng các bài benchmark luôn mang tính giả lập và không phản ánh toàn bộ câu chuyện thực tế. Số liệu dưới đây chỉ nên xem là điểm khởi đầu tham khảo.

Bài benchmark dựa trên dự án langcompilebench của José Valim, đo thời gian biên dịch 100 module, mỗi module chứa 100 hàm trả về chuỗi "hello world". Giai đoạn đầu của việc viết lại bộ sinh mã đã có trong v1.18.0, nên so sánh được thực hiện giữa v1.17.0 và v1.19.0.

Kết quả cho thấy cải thiện đáng kể về thời gian build toàn phần (không dùng cache). Vì Gleam biên dịch theo kiểu tăng dần (incremental), trong quá trình phát triển thông thường tốc độ còn nhanh hơn nữa do không phải biên dịch lại toàn bộ dự án.

Khi so sánh với các ngôn ngữ phổ biến khác trong cùng bài benchmark mở rộng, Gleam cho thấy tốc độ biên dịch khá nhanh — cả khi nhắm tới Erlang lẫn JavaScript. Tất nhiên, đây vẫn là bài test giả lập nên không đủ để đưa ra kết luận cứng rắn về bất kỳ ngôn ngữ nào.

Vì sao không nhắm thẳng tới bytecode BEAM?

Một câu hỏi hợp lý được đặt ra: tại sao không bỏ qua luôn trình biên dịch Erlang và sinh trực tiếp bytecode BEAM? Có thể chúng ta còn tận dụng được thông tin kiểu của Gleam để sinh mã tối ưu hơn nữa.

Tuy nhiên, theo nhóm phát triển Gleam, việc này khó đạt được các lợi ích kể trên. Khác với mã nguồn Erlang và abstract forms, bytecode BEAM không cố định mà có thể thay đổi qua từng phiên bản máy ảo — thêm tính năng mới, đôi khi loại bỏ tính năng cũ. Nếu chọn con đường này, Gleam sẽ phải cam kết bám đuổi mãi mãi với sự phát triển đó, phối hợp chặt chẽ với các nhà bảo trì Erlang để sẵn sàng cho các thay đổi sắp tới. Đồng thời, việc tái tạo toàn bộ các tối ưu mà trình biên dịch Erlang đã tích lũy qua nhiều thập kỷ cũng là nỗ lực khổng lồ.

Gleam là một dự án cộng đồng được tài trợ, không thuộc sở hữu của tập đoàn hay học viện nào, nên cần cân nhắc kỹ cách sử dụng nguồn lực hiệu quả và bền vững nhất. Biên dịch sang Erlang abstract forms hiện là điểm cân bằng tối ưu giữa chi phí và lợi ích. Đáng chú ý là Elixir — ngôn ngữ "đàn anh" được yêu mến — cũng biên dịch sang Erlang qua abstract forms.

Tối ưu cho JavaScript

Không chỉ bộ sinh mã Erlang được cải thiện, phần JavaScript cũng có những nâng cấp đáng kể.

Tối ưu gán nhánh quyết định

Trong Gleam, điều khiển luồng được thực hiện bằng pattern matching qua biểu thức case, và nó được biên dịch thành các câu lệnh if lồng nhau. John Downey đã cải tiến quá trình này để sinh mã phẳng hơn, gộp các if lồng nhau thành một điều kiện duy nhất với ít biến trung gian hơn.

Ví dụ, đoạn Gleam nhỏ:

pub fn go(x) {
  case x {
    Wibble(1, 2) -> 1
    _ -> 2
  }
}

Trước đây sinh ra JavaScript khá cồng kềnh với nhiều tầng if lồng nhau, còn nay chỉ còn:

export function go(x) {
  if (isWibble(x) && x[0] === 1 && x[1] === 2) {
    return 1;
  } else {
    return 2;
  }
}

Điều thú vị là kích thước gói mã sau khi minify và nén gần như không đổi, nhưng mã kết quả có ít nhánh rẽ hơn cho các engine JavaScript tối ưu.

Tối ưu list literal

Kiểu danh sách bất biến (immutable persistent list) của Gleam không giống mảng liền kề có thể thay đổi của JavaScript. Trước đây, mọi list literal đều được biên dịch thành mã tạo mảng rồi chuyển đổi:

const numbers = arrayToList([1, 2, 3])

Nay trình biên dịch sinh mã trực tiếp hơn cho các danh sách ngắn:

const numbers = prepend(1, prepend(2, prepend(3, empty)))

Với các engine JavaScript hiện đại, điều này mang lại cải thiện hiệu năng rõ rệt, đặc biệt có lợi cho các dự án dùng nhiều danh sách ngắn như những dự án dùng thư viện Lustre. Với danh sách dài thì không ghi nhận cải thiện, nên cách chuyển từ mảng vẫn được giữ nguyên.

Overload cho API TypeScript

Khi biên dịch sang JavaScript, Gleam cũng sinh các hàm hỗ trợ làm việc với cấu trúc dữ liệu do lập trình viên định nghĩa, đồng thời cung cấp tệp khai báo TypeScript để tích hợp đầy đủ trong cùng một dự án.

Một trong các hàm được sinh ra là hàm kiểm tra xem một giá trị có thuộc một biến thể cụ thể hay không. Trước đây, khai báo TypeScript sinh ra sẽ làm mất thông tin kiểu, khiến thao tác trở nên cồng kềnh. Giacomo Cavalieri đã thêm overload vào định nghĩa để bảo toàn kiểu bất cứ khi nào có thể:

export function Box$isFull(value: Box$): value is Full;
export function Box$isFull(value: any): value is Full;

Cải thiện cho các công cụ build khác

Người dùng Gleam thường dùng công cụ build chính thức tích hợp trong tệp thực thi gleam, nhưng đôi khi cần biên dịch và dùng mã Gleam trong ngữ cảnh khác. Chẳng hạn, lập trình viên Elixir hay Erlang có thể muốn dùng một gói phụ thuộc viết bằng Gleam. Điều này hoạt động rất tốt ở runtime nhờ khả năng tương tác zero-cost giữa ba ngôn ngữ BEAM, nhưng để đi tới được điểm đó lại khá khó khăn vì các công cụ build chính của Elixir và Erlang chưa hỗ trợ sẵn Gleam.

Phiên bản này bổ sung nhiều cải tiến cho các lệnh dòng lệnh của gleam nhằm hỗ trợ tích hợp vào Mix của Elixir và rebar3 của Erlang:

  • Máy ảo Erlang yêu cầu mọi gói phải có tệp tài nguyên .app cùng bytecode. Trước đây các công cụ build phải tự cung cấp, nay gleam sẽ sinh tệp này khi biên dịch sang BEAM.
  • Lệnh compile-package có thêm cờ --no-dev để chỉ nạp mã từ thư mục src và bỏ qua các gói trong dev_dependencies.
  • Các lệnh export package-information và export package-interface nay có thể in thông tin ra stdout thay vì bắt buộc ghi ra tệp. Tương tự, export javascript-prelude và export typescript-prelude có thể ghi ra tệp.

Language server hỗ trợ nhãn (label)

Gleam có sẵn một language server xuất sắc, cung cấp chức năng IDE cho mọi trình soạn thảo hỗ trợ giao thức language server. Alistair Smith đã bổ sung hỗ trợ đầy đủ cho nhãn của trường và đối số, bao gồm go-to-definition, find-references và đổi tên nhãn — một tính năng tiết kiệm thời gian được nhiều người mong đợi.

Định dạng mã Gleam trong trình duyệt

Gleam có bản build WebAssembly của trình biên dịch, được dùng bởi language tour và playground để biên dịch Gleam ngay trong trình duyệt. John Downey đã thêm hàm format_source mới, cho phép chạy trình định dạng mã Gleam ngay trong trình duyệt. Tính năng này sẽ sớm được bổ sung vào playground.

Thông báo lỗi ngày càng tốt hơn

Việc làm cho thông báo lỗi rõ ràng và hữu ích là ưu tiên hàng đầu của nhóm phát triển Gleam. Bản phát hành này bổ sung nhiều thông báo lỗi hữu ích:

  • 0xda157 thêm lỗi chuyên biệt khi phát hiện dấu hiệu xung đột merge của git trong mã, và một lỗi khác khi cú pháp cập nhật record User(..lucy, score: 10) bị viết sai vị trí bản ghi gốc. Cô cũng thêm lỗi cho các toán tử thủ tục không tồn tại trong Gleam như += và *=.
  • n0kk23 thêm thông báo lỗi hữu ích khi dùng ký hiệu | trong pattern matching theo cách không hợp lệ trong Gleam nhưng hợp lệ ở ngôn ngữ khác như Java.
  • Giacomo Cavalieri thêm thông báo cho các toán tử nhị phân không được phép trong biểu thức hằng, đồng thời cải thiện khả năng chịu lỗi (fault tolerance) của trình biên dịch khi gặp các sai sót này.
  • Andrey Kozhev bổ sung ngữ cảnh cho thông báo lỗi khi một module cố dùng kiểu hoặc giá trị private từ module khác trong cùng gói, cho biết rằng nó tồn tại nhưng là private. Điều này không áp dụng cho các module từ gói phụ thuộc để tránh rò rỉ thông tin.
  • James Dolan cải thiện bộ kiểm tra kiểu để một định nghĩa type alias không hợp lệ không còn gây ra chuỗi lỗi lan truyền qua mọi nơi sử dụng alias đó.

Minh họa tài trợ dự ánMinh họa tài trợ dự án

Lời kêu gọi hỗ trợ

Gleam không thuộc sở hữu của bất kỳ tập đoàn nào, mà hoàn toàn được tài trợ bởi cộng đồng — phần lớn nhà tài trợ đóng góp từ 5 đến 20 USD mỗi tháng. Với tác giả dự án, đây là nguồn thu nhập duy nhất.

Dự án đã đạt nhiều tiến bộ hướng tới mục tiêu trả thù lao thỏa đáng cho các thành viên nhóm nòng cốt, nhưng vẫn còn chặng đường phía trước. Hãy cân nhắc ủng hộ dự án hoặc các thành viên nòng cốt.

Bên cạnh đó, phiên bản này cũng ghi nhận công lao của rất nhiều người sửa lỗi và trau chuốt trải nghiệm người dùng. Chi tiết đầy đủ về các sửa lỗi và cải tiến có trong changelog chính thức của dự án.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗