2 điểm bởi GN⁺ 2024-05-27 | 1 bình luận | Chia sẻ qua WhatsApp
  • Hurl là một ngôn ngữ lập trình thử nghiệm xây dựng luồng điều khiển chỉ bằng xử lý ngoại lệ, thay vì các nhánh hay vòng lặp thông thường
  • Nó bắt đầu từ cuộc trò chuyện giữa Nicole Tietz-Sokolskaya và những người bạn ở Recurse Center, và trang web cung cấp tài liệu cách dùng, ví dụ, hướng dẫn gỡ lỗi và phần hỏi đáp
  • Các lời giới thiệu trên trang mở đầu như “monstrosity is beautiful”, “Certified unhinged™” cố ý đẩy mạnh bầu không khí mang tính đùa cợt
  • Hurl và mã nguồn của trang web được công khai, nhưng để gửi bản vá qua email thì cần chuyển nhượng quyền đối với bản vá
  • Giấy phép được cung cấp theo lựa chọn giữa AGPL-3.0, GAL-1.0 và giấy phép thương mại

Thử nghiệm ngôn ngữ chỉ giữ lại xử lý ngoại lệ

  • Hurl được tạo ra vì một mục đích: khám phá xem liệu có thể tồn tại một ngôn ngữ được cấu thành chỉ từ luồng điều khiển dựa trên xử lý ngoại lệ hay không
  • Ý tưởng xuất phát từ cuộc trò chuyện giữa Nicole Tietz-Sokolskaya và những người bạn ở Recurse Center; danh tính các bạn này không được tiết lộ “để giữ thể diện”
  • Trang web cung cấp tài liệu sử dụng Hurl, ví dụ, hướng dẫn gỡ lỗi và phần hỏi đáp

Trông như trò đùa nhưng là dự án được công khai thật

  • Trang giới thiệu đăng các lời nhận xét thể hiện đúng tính chất của Hurl
    • “This monstrosity is beautiful, and I must never touch it”
    • “Certified unhinged™!”
    • “is "🤮" an available quote?”
  • Nếu muốn đưa thêm lời nhận xét, bạn phải gửi email cho Nicole và cần có sự đồng ý rõ ràng về việc đưa trích dẫn vào

Mã nguồn và điều kiện giấy phép

  • Mã nguồn của ngôn ngữ Hurl và của trang web này được công khai tại Hurl's repo
  • Nếu phát hiện bug hoặc lỗi, bạn có thể gửi bản vá qua email, nhưng phải chuyển giao mọi quyền đối với bản vá đó
    • Đây là điều kiện để duy trì khả năng cấp phép lại và cấp phép thương mại
  • Dự án có thể được sử dụng theo một trong ba giấy phép sau
    • AGPL-3.0

      • GAL-1.0: Gay Agenda License
      • Giấy phép thương mại
      • Trong quá trình xem xét giấy phép, dự án cũng đã cân nhắc joke licensesunfortunate licenses, nhưng cuối cùng chọn ba giấy phép trên

1 bình luận

 
GN⁺ 2024-05-27
Các ý kiến trên Hacker News
  • Nếu thiết kế một ngôn ngữ lập trình, có lẽ nên bắt buộc include/import phải có namespace, và nếu có thể thì cũng nên chặn các side effect ở cấp cao nhất
    Nhận kiểu let foo = include "lib/foo.hurl" rồi gọi foo.init() sẽ dễ suy luận hơn nhiều
    Ngược lại, nếu sau include "lib/foo.hurl" // side effects lại xuất hiện baz(buz), sẽ khó biết hàm và biến đó thuộc thư viện chuẩn hay được include từ đâu đó

    • Khi không có binding tên tường minh, tốt hơn là nên bắt buộc câu lệnh import khớp với namespace
      import "foo/bar" nên cho phép dùng foo.* hoặc bar.*, chứ không nên xuất hiện bazz.*. Nghĩ ngay đến Go
    • Không phải là không đồng ý, nhưng IntelliJ mình dùng trong công việc hiển thị rất rõ một tham chiếu được import từ đâu, và cũng có thể nhảy đến đó bằng phím tắt
      VSCode cũng làm được tương tự bằng plugin và LSP, nhưng kém hơn nhiều. Việc điều hướng code quá chậm nên mình không thể làm việc bằng VSCode
      Mình tự hỏi những đề xuất như vậy chỉ hữu ích khi không có các công cụ đó hay không. Ít nhất trong môi trường chuyên nghiệp, sống thiếu những công cụ như vậy có vẻ là bất khả thi
    • Làm vậy thì có thể truyền tham số cho foo.init(), còn import trần thì không thể
    • Nếu một ngôn ngữ có flow control xoay quanh exception, thì có lẽ con tàu “dễ suy luận” đã rời bến rồi
      Nói vậy không có nghĩa dự án này vô giá trị; ngược lại, mình xem nó như một tác phẩm nghệ thuật
    • Đồng ý 100%
      Mình từng fork Ruby để làm cho require không ghi đè bảng symbol, nhưng có vẻ hệ sinh thái Ruby phụ thuộc quá nhiều vào trạng thái mutable toàn cục dùng chung, nên cuối cùng mình mất hứng thú với chính Ruby
  • Exception luôn khiến việc nắm bắt hợp đồng giữa caller và callee trở nên khó hơn, đồng thời làm tăng độ coupling của code, nên mình chưa bao giờ thích
    Mình thích cách xử lý bằng giá trị trả về như Go hay Rust hơn. Khi lướt qua ngôn ngữ này, mình không chắc có yếu tố nào giải quyết vấn đề đó không
    Nếu IDE có thể động xác định tất cả exception chưa được bắt của một hàm, và cho nhảy đến những vị trí exception có thể được ném ra, thì mô hình này cũng có thể ổn. Nhưng mình không biết sẽ xử lý coupling thế nào, và có vẻ control-flow graph sẽ cực kỳ bất ổn

    • Trong Java, IntelliJ làm đúng việc đó. Nếu có hàm ném exception và ở đâu đó trong dự án caller không bắt, nó sẽ đánh dấu là vấn đề, đồng thời có thể dễ dàng đi đến implementation hoặc vị trí gọi
      Tuy nhiên trong Java, trừ khi kế thừa RuntimeException, các exception mà hàm có thể ném là một phần của chữ ký hàm. Trong trường hợp đó, nếu ném exception mà không thêm vào chữ ký thì sẽ không biên dịch
      Điều kiện của Java khiến IDE dễ báo cáo exception chưa được bắt hơn nhiều, nhưng nếu không phải runtime exception thì đây là vấn đề có thể giải bằng phân tích tĩnh
      Ngược lại, cách trả về giá trị bọc Ok/Err đã chuẩn hóa có vẻ đơn giản hơn cả về hỗ trợ công cụ lẫn sự tiện lợi cho lập trình viên
    • Thực sự không có bất kỳ khác biệt nào giữa ném exception và trả exception như một biến
      Khác biệt duy nhất là với cách truyền exception, phần boilerplate code mà compiler sẽ làm hộ thì bạn lại phải tự viết bằng tay
      Mình thật sự không thể hiểu nổi vì sao vào năm 2024, một người đầu óc hoạt động bình thường lại muốn tự tay làm việc đó
    • Mình có lẽ đã thích Go hơn nếu nó không tự nhiên nuốt mất ngữ cảnh lỗi
      Trong thời gian dùng nó, việc tìm nguyên nhân gốc khi không có debugger đau khổ hơn nhiều
  • Ví dụ toss nói rằng “chủ yếu dùng để truyền nhiều giá trị ra khỏi hàm, không thật sự cần thiết nhưng dễ thương”, nhưng nó hoàn toàn không vô dụng; đây là một cách triển khai generator có thể tiếp tục
    Tất nhiên, nếu muốn nó làm việc khác ngoài việc tiếp tục ngay lập tức thì có thể khá thú vị. Chỉ cần tổ chức toàn bộ codebase thành stack bên trong-bên ngoài của toss là được

    • Không hoàn toàn giống. Generator có thể tiếp tục có thể được tiếp tục ở bất kỳ điểm nào về sau trong chương trình, còn ở đây return phải được scope theo lexical về phía handler
      Ví dụ trong Python, có thể gọi next() ở bất cứ đâu
      Cái này gần với việc truyền callback qua một kênh phụ hơn. toss gọi callback đó, còn return theo đúng nghĩa là trả về từ đó
    • Sau khi đọc xong, suy nghĩ đầu tiên của mình cũng đúng là như vậy
      Một tính năng thật sự hữu ích trong ngôn ngữ khác lại được đối xử ở đây như một tính năng dễ thương có thể bỏ đi, khiến mình hơi tự hỏi có phải đây là một trò đùa không
    • Nó gần với lan truyền sự kiện dựa trên stack hơn
    • Có giống yield của C# không?
    • Mình cũng nghĩ vậy đầu tiên. Một ngôn ngữ nhỏ gọn hay ho có generator, nghe ổn đấy
  • Hurl trông khá gần với condition system kiểu Smalltalk hoặc Common Lisp
    Unwinding stack và resume chỉ là hai trong số các restart khả dĩ: https://gigamonkeys.com/book/beyond-exception-handling-condi...

  • Tách khỏi bản thân dự án, mình tin chắc rằng thế giới sẽ tốt đẹp hơn nếu nhiều thứ hơn dùng đuôi .wtf cho domain

  • Điều này nghe giống một dạng yếu của algebraic effects, nhưng vẫn rất thú vị khi thấy những ngôn ngữ như thế này và những gì có thể làm với chúng

    • Mình chưa từng hiểu algebraic effects, nhưng tài liệu Hurl thì hiểu được
      Nếu algebraic effects về cơ bản giống từ khóa toss của Hurl, thì mình tò mò nó mạnh hơn toss ở điểm nào
  • Một thử nghiệm tư duy thú vị
    Mình thật sự ghét exception, và muốn một ngôn ngữ không có exception
    Exception là goto của thời đại chúng ta
    Khi đã có Maybe/OptionEffect/Result, gần như không còn lý do gì để ném exception rồi phải lần theo trong đầu xem nó được xử lý ở đâu
    Mình hơi lo rằng algebraic effects sẽ phổ biến hơn, và sẽ ảnh hưởng cả đến frontend JS vốn đã quá phức tạp. Vì nó khuyến khích cách ném “exception” để flow control
    Nếu tất cả những thứ này nhằm tránh vấn đề màu sắc async/sync, thì cá nhân mình thấy hoàn toàn không đáng

  • Chà, mình không thích cái này. Nhưng kỳ lạ là nó gần như có một vẻ thanh nhã
    Rất khó mô hình hóa trong đầu, nhưng vẫn là vậy
    Nói nghiêm túc hơn một chút, mình muốn có các cú pháp catch khác nhau cho exception có thể resume và exception không thể resume. Như vậy sẽ loại bỏ sự mơ hồ cú pháp về việc return có chuyển luồng điều khiển về phía đã ném exception tức thời gần nhất hay không
    Và thư viện chuẩn không nên chạy trốn sang các hàm trả về giá trị thông thường. Không có lý do gì để không ăn món mình tự nấu chỉ vì ăn thử thấy ợ nóng

    • Đúng. Tuy nhiên, một lựa chọn khác là có thể làm cho cú pháp tự động catch khi gọi hàm ở vị trí đang kỳ vọng một giá trị
  • Mình hiểu đúng là thứ bị hurl thì có thể bắt, còn thứ bị toss thì không thể bắt phải không? Chắc sẽ mất một thời gian để quen
    Mình cũng hơi lo không biết phải viết bao nhiêu Hurl thì mọi người bắt đầu gọi mình là tosser

  • Toss nghe như một cấu trúc ngôn ngữ thú vị. Nó đi ngược lên stack để tìm exception handler, rồi quay lại vị trí ban đầu và tiếp tục thực thi như chưa có gì xảy ra
    Có vẻ có thể dùng cấu trúc này để tiêm hành vi bổ sung vào runtime
    Trong code hướng đối tượng thông thường, dependency injection được thực hiện qua constructor của service, còn toss có cho phép làm điều đó bằng “toss handler” không?

    • Hãy xem Koka. Đó là một ngôn ngữ “thật” có hệ thống algebraic effects
      https://koka-lang.github.io/koka/doc/book.html#why-handlers
    • Khá giống condition system của Common Lisp
      Mình không biết nhiều, nhưng biết rằng nó cho phép tiêm hành vi vào runtime theo kiểu này
    • Giống Resume Next của VB