2 điểm bởi GN⁺ 6 giờ trước | 2 bình luận | Chia sẻ qua WhatsApp
  • Cú pháp JavaScript dễ trở nên phức tạp với các dấu ngoặc lồng nhau và callback, đồng thời gây ra tình trạng phình to khi kéo quá nhiều thư viện vào chỉ cho một UI nhỏ
  • WebAssembly mở ra con đường chạy các ngôn ngữ khác trong trình duyệt, nhưng chi phí kết nối bất đồng bộ với vòng lặp sự kiện JavaScript như trong Pyodide là rất lớn
  • Tài nguyên trình duyệt và bộ nhớ WebAssembly đều có giới hạn, vì vậy cần một cách tiếp cận đàm phán thay vì cố thay thế JavaScript
  • LispE chứa hơn 450 hàm trong một tệp nhị phân WASM 3.3MB, đồng thời cung cấp xử lý chuỗi, tính toán, ma trận và biểu thức chính quy
  • Với evaljsasyncjs, có thể tận dụng hàm JavaScript và DOM, đồng thời giảm độ phình của mã nguồn bằng một tệp nhị phân duy nhất có thể kiểm toán thay cho nhiều thư viện bên ngoài

Độ phình của JavaScript và các ràng buộc của trình duyệt

  • Cú pháp JavaScript dễ trở nên phức tạp vì dấu ngoặc đơn, ngoặc nhọn và ngoặc vuông chồng chéo, buộc phải khớp đúng thứ tự đóng
    • Bài viết đưa ra ví dụ chèn giá trị vào nhiều đối tượng bên trong callback của forEach và cập nhật cache có điều kiện
newNames.forEach((name, i) => {
    allAgentContents[name] = contents[i];
    agentModes[name] = modes[i];
    if (compiled[i]) agentCompiledCache[name] = compiled[i];
    agentViewingCompiled[name] = viewing[i];
});
  • Để xử lý những tác vụ mà một ngôn ngữ lập trình cơ bản thường có sẵn, JavaScript lại thường phải tải về rất nhiều thư viện
    • C hay C++ cũng cần include để làm việc, nhưng với thư viện JavaScript thì thường khó biết ai là người triển khai và ai đang bảo trì
    • Có những trang trông như phải tải “một nửa Internet” chỉ để hiển thị hello trong một cửa sổ nhỏ
  • Internet phụ thuộc vào JavaScript; dù có che đi bằng TypeScript thì khi mở trang trong trình duyệt, nó vẫn là người gác cổng bắt buộc phải đi qua
  • Từng có giấc mơ rằng trình duyệt sẽ trở thành hệ điều hành tối thượng, khiến Windows hay Mac OS không còn cần thiết, và JavaScript được chọn làm ngôn ngữ để hiện thực hóa giấc mơ đó

Quan hệ giữa WebAssembly và JavaScript

  • WebAssembly gần với một máy ảo có mã máy riêng, mở ra khả năng viết mã theo cách khác bên trong trình duyệt
  • Pyodide là một thành tựu kỹ thuật ấn tượng để chạy Python trong trình duyệt, nhưng cũng cho thấy cái giá của việc vận hành bên trong vùng JavaScript
    • Hai thế giới bất đồng bộ là asyncio của Python và vòng lặp sự kiện JavaScript phải giao tiếp với nhau
    • Cây cầu giữa hai thế giới này mong manh, và phải luôn nhớ mỗi await thuộc về thế giới nào
  • Tài nguyên trình duyệt có giới hạn, nên cần cách tư duy như thuở đầu của khoa học máy tính: soi từng lệnh và từng cấu trúc ở mức bit
    • Tác giả lấy mốc là đã bắt đầu lập trình vào năm 1981 trên một máy tính chỉ còn đúng 15772 bytes trống
    • Bộ nhớ trình duyệt hiện đại không bị giới hạn như chiếc máy tính đầu tiên đó, nhưng WebAssembly cũng không thể tự do sở hữu bộ nhớ như chương trình thông thường mà phải xin cấp phát theo cách bị giới hạn trước
  • Thái độ cốt lõi không phải là đối đầu với JavaScript mà là đàm phán với nó

Phương án thay thế mà LispE đưa ra

  • LispE cung cấp hơn 450 hàm trong một tệp nhị phân duy nhất
    • Kích thước tệp nhị phân WASM là 3.3 MB
    • Các chức năng xử lý chuỗi, tính toán, ma trận và biểu thức chính quy được gom vào một chỗ
    • Tệp nhị phân WASM nằm tại binaries/wasm
  • Đây là một trình thông dịch nhỏ nhưng có thể xử lý chuỗi, Float64Array, số nguyên, số thực và mảng chuỗi làm giá trị trả về
  • LispE dựa trên Lisp nên có cấu trúc AST còn sống
  • Nếu không hợp với cú pháp Lisp, có thể dùng cú pháp chuyển đổi dành cho một ngôn ngữ khó phân biệt với Python hoặc Basic
    • Có thể sửa grammar để tạo ra ngôn ngữ theo phong cách mong muốn
    • Thậm chí có thể dùng tiếng Hy Lạp, và đã có ví dụ tiếng Hy Lạp
  • LispE không đối đầu với JavaScript mà cộng tác bằng cách tận dụng các hàm JavaScript và chức năng DOM

Gọi JavaScript: evaljs và asyncjs

  • Để chạy mã JavaScript bên trong LispE, có thể dùng evaljsasyncjs
    • evaljs thực thi mã JavaScript và nhận lại giá trị
    • asyncjs kết nối các hàm JavaScript do người dùng định nghĩa trên trang với callback bất đồng bộ
(setq a (evaljs "10 + 20 + 30")) ; execute some JS code

; call_llm is a user-defined JS function in the page
(asyncjs `call_llm("Implement a piece of code in Python to sort strings");` 'mycallback)

(defun mycallback(theresult) ...)
  • asyncjs hoạt động như một Promise rằng nó sẽ quay lại khi tác vụ hoàn tất

Giảm độ phình của mã nguồn

  • Trích dẫn của Wirth năm 1995 dẫn tới kết luận rằng máy tính nhanh hơn và mô hình lớn hơn không giải quyết được vấn đề; mã nguồn cần được giữ gọn nhẹ
  • LispE cung cấp 450 hàm được đưa ra trình duyệt dưới dạng một tệp nhị phân duy nhất có thể kiểm toán
    • Nếu triển khai riêng từng chức năng thì phải tải các thư viện như mathjs cho tính toán số, lodash cho collection, voca cho thao tác chuỗi, simple-statistics cho phân phối thống kê
    • Cách này có thể phình thành hàng trăm MB mã nguồn từ các tác giả khác nhau, với lỗi và lịch bảo trì riêng
  • LispE cung cấp các chức năng này trong một mã nguồn duy nhất được bảo trì, và vì là mã nguồn mở nên có thể kiểm toán toàn bộ
  • Tác giả cho rằng Garbage Collector sẽ không làm sụp hiệu năng mã ở đúng thời điểm tệ nhất
  • Nó giao tiếp minh bạch với JavaScript bằng các lời gọi API đơn giản và có thể trả về cấu trúc được định nghĩa sẵn
// floats here is a Float64Array
const floats = callEvalLispEToFloats(0, `(normal_distribution 100)`);

2 bình luận

 

Ban đầu tôi thấy bài viết hơi đột ngột nên còn nghi ngờ nguồn, ai ngờ lại là Naver, thật luôn.
Nhưng đúng là phản ứng vẫn không tốt lắm... Nói thật thì việc JavaScript bị thổi phồng đến mức còn đưa cả WASM 3.3MB lên chỉ để dùng Lisp đúng là kiểu over-engineering khó mà hiểu nổi, ha ha ha.

Về lý do dùng tài khoản Naver, một nhà phát triển của dự án tên Claudius đã để lại bình luận rằng anh ấy đang làm việc tại Naver Labs Europe và bài đó được đăng lên vì Naver đã phê duyệt nó như một dự án mã nguồn mở.
Có vẻ cũng không liên quan nhiều đến Naver, chắc chỉ là những người thật sự rất yêu Lisp thôi...

 
Ý kiến trên Lobste.rs
  • Bảo là sẽ làm JavaScript bớt phình mà lại thêm một khối WASM 3.3MB mù mờ, rồi bảo viết app bằng Lisp nữa sao?
    Chỉ với JavaScript thuần và 0 phụ thuộc bổ sung thôi cũng có thể làm được khá nhiều thứ trước khi chạm đến 1/10 kích thước đó

    • Mong là đừng chỉ nhìn một phần nhỏ của tài liệu rồi gạt đi
      Tôi cũng không đồng tình với cách dùng này, nhưng đây là một dự án đam mê về Lisp thú vị, có nét kỳ quặc hiếm thấy dạo này, và tôi thích việc toàn bộ tài liệu đều có cảm giác như được viết tay với văn phong rất riêng
  • Những thứ đáng đưa mới vào thư viện chuẩn thì lúc nào cũng được đề xuất
    Các bổ sung gần đây có hợp/ giao của tập hợp, sum, base64, v.v.
    Tuy vậy, tôi hầu như không nghe yêu cầu nào về các hàm giải tích, còn phía chuỗi thì các yêu cầu lặp đi lặp lại thường là .reverse hoặc .titleCase
    .reverse không có nhiều trường hợp dùng thật sự thuyết phục ngoài các bài toán đồ chơi, còn .titleCase thì có thể làm được nhưng cần dữ liệu quốc tế hóa nên vẫn đang được thảo luận
    Hơn nữa, cả hai thứ đó cũng không có trong thư viện này

    • Có vẻ nhiều người không quen ai trong tc39 hoặc không biết rằng họ ở rất gần với cộng đồng của chúng ta
      Họ cảm thấy dù có đề xuất thì cũng phải mất 3 năm mới được đưa vào thật, nên không đáng để thử, còn thư mục utils/ tuy không hay nhưng lại là thứ có thể làm ngay
    • Thư viện chuẩn đã thay đổi nhiều hơn mức tôi theo dõi, nên có lẽ tôi cần sửa bài một chút
      Luận điểm của tôi nghiêng về mô hình phân phối hơn là việc JavaScript thiếu hàm
      Dù thư viện chuẩn có đầy đủ đến đâu, ứng dụng trung bình vẫn sẽ mang theo hàng megabyte phụ thuộc npm bắc cầu
      LispE-as-WASM là 3.3MB, khoảng 450 hàm đã được tài liệu hóa, lõi C++, mã nguồn công khai trên GitHub, là một thử nghiệm để xem một runtime có thể kiểm toán trông sẽ như thế nào
      Tôi thực sự đã dựng một agent harness trên đó và đang chạy nóng các quy tắc LispE ngay trong trình duyệt
      Vòng lặp sự kiện của JavaScript thì tôi rất thích
  • Mỗi khi ai đó nói cú pháp bị phình là tôi lại thấy căng
    Cú pháp tối giản có thể khó đọc hơn nhiều so với cú pháp dùng các ký tự khác nhau để phân biệt trực quan
    Nếu nhìn vào thứ LispE đưa ra như một lựa chọn thay thế thì nó thế này:

    (defun gol8(⍵) ((λ(⍺) (| (& (== ⍺ 4) r) (== ⍺ 3))) (⌿ '+ (⌿  '+ (° (λ (x ⍺) (⊖ ⍺ x)) '(1 0 -1) (↑ (λ (x) (⌽ ⍵ x)) '(1 0 -1)))))))  
    

    ... !?
    https://github.com/naver/lispe/wiki/5.3-A-la-APL

    • Đây dĩ nhiên là bản port của ví dụ APL nổi tiếng vì độ ngắn gọn của nó https://aplwiki.com/wiki/Conway's_Game_of_Life
    • Ồ, kiểu này thì tôi sẽ không cho phép trong bất kỳ môi trường production nào
  • Điểm vào của dự án này hơi kỳ quặc, còn trang https://github.com/naver/lispe/wiki/1.-Introduction có vẻ tốt hơn
    Đây là một dự án Lisp native cũng nhắm tới WASM, và có vài thứ vui vui mà những người mê ngôn ngữ lập trình có thể thích

  • Ấn tượng đầu tiên của tôi là đây là một ví dụ JavaScript khá gượng ép
    Phần lớn sự ghét bỏ JavaScript có lẽ đến từ thời kỳ đầu với bất tương thích giữa các trình duyệt, DOM API khó chịu, và những cái bẫy cú pháp, nhưng những thứ đó thực ra gần như không còn ảnh hưởng đến công việc hằng ngày của tôi nữa
    JavaScript hiện đại, đặc biệt là khi dùng TypeScript và các quy tắc lint hợp lý, thì với tư cách một ngôn ngữ là hoàn toàn ổn
    Vẫn còn các vấn đề như CJS với ESM, rủi ro chuỗi cung ứng, hay sự biến động của hệ sinh thái, nhưng phần lớn nằm ngoài bản thân ngôn ngữ

  • Cái trải nghiệm mà người ta phải làm hẳn extension cho editor chỉ vì việc cân bằng dấu ngoặc quá phiền toái, vậy mà lại được coi là tuyệt vời sao
    Nếu muốn kiểu lập trình này, tức là mật độ cú pháp thấp và thiên hướng hàm, thì tốt hơn nên đảo hướng và chuyển sang ngôn ngữ concatenative

  • Tôi thắc mắc làm sao kết quả biên dịch của một trình thông dịch Lisp lại có thể lên tới 3.3MB
    Thế này là bớt phình sao?

    • Có lẽ là vì nó bao gồm cả thư viện chuẩn lớn
      Dù vậy, tôi đồng ý rằng việc phân phối kèm một thư viện chuẩn lớn có cả mã không dùng đến thì còn lâu mới gọi là bớt phình
  • Chấn động! Kinh hoàng!
    Phải đóng dấu ngoặc theo đúng thứ tự cơ đấy, thật bất ngờ

    • Nếu muốn một ngôn ngữ mà không cần đóng đúng thứ tự thì có J
      Ngay cả những người yêu Lisp vốn phát ngán sự dài dòng của các ngôn ngữ như JavaScript cũng thường quay xe khi đứng trước các ngôn ngữ họ APL, rồi bắt đầu tranh luận rằng sự rõ ràngtính dễ đọc quan trọng hơn nhiều so với việc ngắn hết mức có thể
      Có vẻ tác giả LispE cũng có chút yêu thích các phép toán cơ bản của APL, nhưng vẫn khó hiểu vì sao khi đã biết đến một ngôn ngữ có ký pháp cô đọng như thế, họ lại thích một phiên bản Conway Life dài hơn, thưa hơn, với các s-biểu thức lồng sâu hơn bản APL, J, K, thậm chí cả Q