Raku, một ngôn ngữ dành cho gremlin
(buttondown.email)- Raku từng được xem xét như một ứng viên ngôn ngữ máy tính bỏ túi để lấp khoảng trống trong các tác vụ toán học của Python, J, Frink và Excel; chỉ qua một thử nghiệm ngắn cũng đã tạo ấn tượng là một ngôn ngữ rất mạnh nhưng kỳ quặc
- Có phạm vi sức biểu đạt xoay quanh toán tử rất rộng, như ký hiệu Unicode, toán tử trung tố gồm chữ và số, tích danh sách・zip・reduce・tích lũy, matcher
~~, chuỗi... - Người dùng không chỉ có thể định nghĩa toán tử trung tố mà còn cả toán tử circumfix/postcircumfix, và cũng có thể chỉ định tính kết hợp vượt ra ngoài kết hợp trái・phải, gồm kết hợp dạng chuỗi và dạng danh sách
- Đa điều phối không chỉ rẽ nhánh theo chữ ký kiểu mà còn theo vị từ runtime
where; chữ ký hàm và cả các tham số bên trong chữ ký cũng có thể được xử lý như giá trị hạng nhất - Việc bảo trì codebase lớn có vẻ sẽ là gánh nặng, nhưng Raku hấp dẫn với lập trình quy mô nhỏ như script dùng một lần, tính toán, công cụ cá nhân; tài liệu, REPL trên Windows, tốc độ biên dịch và lỗi sigil là các trở ngại chính
Cơ duyên tìm hiểu Raku và ấn tượng đầu tiên
- Raku là ngôn ngữ trước đây được biết đến với tên Perl 6
- Sau khi viết về những bất mãn với ngôn ngữ động, một số người dùng đã gợi ý Raku, nên tôi tìm hiểu để xem nó có phù hợp làm ngôn ngữ máy tính bỏ túi cho các tác vụ toán học hay không
- Trước đó tôi dùng kết hợp Python, J, Frink, Excel, nhưng mỗi thứ đều có nhược điểm lớn
- Sau vài ngày thử nghiệm, ấn tượng nhận được gần giống như “một ngôn ngữ được thiết kế bởi những gremlin thật sự thông minh, sau khi thu thập rất nhiều phản hồi từ các gremlin khác”
Hệ thống toán tử khác thường
- Raku tích cực sử dụng toán tử Unicode
- Kiểm tra một phần tử có thuộc tập hợp hay không bằng
∈ - Cũng có
∉,∋,∌
- Kiểm tra một phần tử có thuộc tập hợp hay không bằng
- Cũng cho phép toán tử trung tố gồm chữ và số
- Toán tử lặp chuỗi là
x - Hợp thành hàm là
o
- Toán tử lặp chuỗi là
- Tổ hợp danh sách cũng được biểu diễn bằng ký hiệu ngắn
Xtạo tích Descartes của danh sáchXfáp dụngfcho từng phần tử của tích DescartesZflàm việc tương tự theo kiểu zip
- Với toán tử trung tố
f,[f]reduce một danh sách, còn[\f]tạo kết quả tích lũy[+] <1 2 3 4 5>là15[\+] <1 2 3 4 5>là(1 3 6 10 15)
Matcher ~~ và chuỗi ...
~~được dùng như một matcher xử lý nhiều kiểu so sánh bằng cùng một cú pháp"abc" ~~ "abc"kiểm tra chuỗi có khớp hay không"abc" ~~ Strkiểm tra có phải kiểu chuỗi hay không"abc" ~~ {.chars == 3}kiểm tra độ dài có bằng 3 hay không"abc" ~~ /^b/kiểm traabccó bắt đầu bằngbhay không
...nắm bắt mẫu từ các giá trị phía trước để tạo chuỗi0,1,2...10tăng từng bước 1 từ0đến100,2,4...10trở thành chuỗi số chẵn1,2,4...10đi theo mẫu tăng như1 2 4 8
Toán tử do người dùng định nghĩa
- Raku không chỉ dừng ở việc định nghĩa toán tử trung tố như một số ngôn ngữ, mà còn có thể tạo toán tử circumfix và postcircumfix
- Ví dụ, có thể định nghĩa một toán tử bao quanh như
sub circumfix:<[∀ zz>($inner){sum($inner)}để cộng các giá trị bên trong - Cũng có thể định nghĩa toán tử postcircumfix trông giống tích vô hướng vector
sub postcircumfix:<| ⟩>(@left, @inside){[+] (@left Z* @inside)}<1 2 3>|<4 5 6>⟩là32
- Tính kết hợp của toán tử cũng có thể được chỉ định theo nhiều cách
- Có thể định nghĩa toán tử trung tố kết hợp trái・kết hợp phải thông thường
- Có thể chỉ định kết hợp dạng chuỗi, trong đó
x < y < zđược diễn giải nhưx < y && y < z - Cũng hỗ trợ kết hợp dạng danh sách, trong đó
a op b op ctrở thànhop(a, b, c)
Đa điều phối và rẽ nhánh theo điều kiện runtime
- Raku hỗ trợ đa điều phối, chọn định nghĩa phù hợp trong số nhiều định nghĩa hàm có chữ ký kiểu khác nhau
- Hàm ví dụ
fhoạt động khác nhau tùy theo tổ hợp đối số- Nếu nhận một scalar và một mảng, nó cộng scalar vào từng phần tử của mảng
- Cũng hỗ trợ trường hợp nhận mảng và scalar
- Nếu nhận hai mảng, nó cộng từng phần tử bằng
Z+
- Điểm đặc biệt hơn là cũng có thể điều phối theo vị từ runtime của giá trị
multi my_abs(Int $x where {$x > 0}) {$x}multi my_abs(Int $x) {-$x}
- Chữ ký của hàm là giá trị hạng nhất, và các tham số bên trong chữ ký cũng là giá trị hạng nhất
Bề mặt rộng được tạo nên từ những tính năng nhỏ
- Nếu định nghĩa hàm
MAIN, các tham số của nó sẽ tự động được chuyển thành cờ CLI - Đối tượng có rất nhiều phương thức được cung cấp sẵn
- Đối tượng List cung cấp các phương thức để lấy mọi hoán vị, mọi tổ hợp k phần tử, và mọi cửa sổ trượt
- Junctions là một dạng giá trị đặc biệt để thực hiện nhiều so sánh cùng lúc
1|2được mở rộng thànhany(1, 2), nên1 < 1|2đúng1&2được mở rộng thànhall(1, 2), nên1 !< 1&2đúng
- Bất kỳ toán tử trung tố nào cũng có thể trở thành toán tử phủ định bằng cách thêm
!phía trước - Raku có vẻ là một ngôn ngữ vừa có tên dạng
$kebab-casevừa có phép trừ trung tố, và có lẽ sigil giúp phân biệtx-y - Cú pháp regex không tương thích ngược với Perl 5
- Trong 30 năm, các ngôn ngữ đã đi theo “chuẩn” PCRE, nhưng Perl 6 đã bỏ nó
Những phần chưa xem xét và sức hấp dẫn ở quy mô nhỏ
- Những gì đã xem chỉ là một phần tính năng xoay quanh mục đích dùng như máy tính bỏ túi
- Tôi vẫn chưa học hệ thống đối tượng, package, grammars
- Cũng còn nhiều tính năng bị bỏ sót, như
samewithtrong thân hàm gọi lại cùng hàm đó với đối số mới - Nếu phải bảo trì một codebase Raku legacy thì có lẽ sẽ rất vất vả, nhưng Raku trông mạnh mẽ cho kiểu lập trình In The Small
- Script dùng một lần
- Tính toán
- Công cụ cá nhân
- Loại công việc ban đầu tôi mong muốn
Bất mãn và kỳ vọng
- Tài liệu rất thiếu, và do phụ thuộc nhiều vào ký hiệu nên khó tìm kiếm
- Dù đã có kinh nghiệm học nhiều ngôn ngữ thiếu tài liệu, Raku còn lớn và phức tạp hơn nhiều, nên có thể làm giảm động lực
- Trên Windows, nhập Unicode vào REPL sẽ gây crash
- Trình biên dịch cũng khá chậm, ngay cả file nhỏ cũng mất hơn 0,5 giây, khiến các tác vụ lặp đi lặp lại trở nên khó chịu
- Hệ thống sigil gây bất tiện
- Có trường hợp mất 30 phút debug chỉ vì dùng
$xthay cho@x
- Có trường hợp mất 30 phút debug chỉ vì dùng
- Nhìn chung tôi thích Raku và mong nó thành công, nhưng hy vọng theo thời gian thời gian biên dịch và tài liệu sẽ được cải thiện
1 bình luận
Ý kiến trên Hacker News
Nếu đặt các ngôn ngữ lập trình lên một không gian hai chiều, các trục có thể là mức độ khiến ta ngạc nhiên và, khi đã ngạc nhiên, liệu điều đó thú vị hay kinh khủng
Với các ngôn ngữ bình thường, người ta thường ngầm kỳ vọng chúng nằm ở góc dưới bên trái: “hầu như không gây ngạc nhiên, nhưng nếu thỉnh thoảng có thì cũng là ngạc nhiên thú vị”. Còn Raku thì có cảm giác công khai nhắm tới góc trên bên trái vốn hiếm khi có ai đứng vào. Gần như là thái độ “Lạ nhỉ? Không phải rất hay sao?”
document.write = function ..., và xét ở chỗ nó làm được việc cần làm thì khá thú vị, nhưng đồng thời cũng khá kinh khủngPerl cũng có nhiều “bất ngờ thú vị”, và tôi nghĩ Raku chủ yếu được thiết kế để loại bỏ những bất ngờ kinh khủng của Perl
∈để kiểm tra một phần tử có thuộc tập hợp hay không thì tôi hiểu ý rồi0,2,4...10trở thành(0 2 4 6 8 10), nhưng việc1,2,4...10thành(1 2 4 8)lại khiến tôi tự hỏi “nó đang tìm số tiếp theo trên OEIS à?”Raku là một ngôn ngữ thú vị, nhưng một số idiom của nó không dễ đi vào đầu
Giống như AppleScript từng cố trông giống ngôn ngữ tự nhiên nhưng lại tạo cảm giác kỳ quặc, Raku pha trộn các yếu tố mang hơi hướng ngôn ngữ tự nhiên như
my,say,sub,gathervới các ký hiệu như@, khai báo module và những quyết định cú pháp mà với người ngoài trông khá Byzantine. Ví dụ 99 bottles về mặt logic thì vẫn lần theo được, nhưng khó mà tự trực giác phát hiện ra. Cảm giác có nhiều ký hiệu và chúng bị quá tải tùy theo ngữ cảnh, nên ngay cả với những việc mà Raku có thể tối ưu, như parser ngôn ngữ tự nhiên, tôi cũng không muốn trực tiếp dùng nóhttps://examples.raku.org/categories/module-management/Fletc...
@biểu thị mảng, sẽ trông rất quenTính năng tôi thích nhất ở Raku là phép chia số nguyên và các literal thập phân đều trả về
Rat, một kiểu số hữu tỉAi cũng biết số thực dấu phẩy động không hay lắm, nhưng rất ít ngôn ngữ thực sự cố thoát khỏi nó; trong Raku, phải dùng ký pháp khoa học thì literal mới là số thực dấu phẩy động
Chúng có một hệ phân cấp số bao gồm cả số hữu tỉ và số hữu tỉ phức, và dĩ nhiên cũng hỗ trợ số có độ chính xác tùy ý. Khả năng kết hợp rất tuyệt
Rattrở nên quá lớn, nó sẽ tự động chuyển sang số thực dấu phẩy động1/10làRat, nhưng1/100000000000000000000lại làNum. Cũng cóFatRatkhông bị nâng cấp như vậy, nhưng đó không phải mặc địnhVì vậy, thay vì nói rằng “nó đã thoát khỏi cách dùng số thực dấu phẩy động không chính xác dù ta không yêu cầu rõ ràng”, đúng hơn là ngay từ đầu nó chưa từng ở trong tình trạng đó
(/ 1.0 3.0)là0.3333333333333333,(/ 1 3)là1/3, và(- (+ 0.1 0.2) 0.3)là5.551115123125783e-17float()nhiều hơnDecimal()rất nhiềuNếu không trực tiếp xử lý tiền bạc thì hầu như lúc nào số thực dấu phẩy động cũng là lựa chọn mong muốn
Tôi không thấy tài liệu Raku “thật sự tệ”; ngược lại, trang tài liệu chính thức gây ấn tượng vì là nguồn một cửa chứa cả tài liệu khái niệm lẫn tài liệu API
https://docs.raku.org/
Trang này là điểm khởi đầu rất tốt cho tài liệu khái niệm: https://docs.raku.org/language
Phần lớn nội dung hiện có được viết tốt và có các ví dụ mã hữu ích, nhưng thỉnh thoảng tôi gặp những phần hoàn toàn chưa được ghi tài liệu hoặc chỉ xử lý các trường hợp đơn giản. Đặc biệt hệ thống module là vấn đề lớn nhất, và chỉ đọc trang Modules thì khó hiểu được sự khác nhau giữa module/package. Namespace được import và namespace được khai báo có thể khác nhau, nhưng để compiler tìm thấy thì cấu trúc thư mục phải khớp với namespace; điều này hữu ích và kỳ lạ là cũng có lý, nhưng tôi đã phải tự mò qua thực hành mới học được
Các toán tử Unicode đẹp mắt của Raku đều có cách viết thay thế bằng ASCII
Ví dụ cách viết thay thế cho
∈,∉,∋,∌lần lượt là(elem),!(elem),(cont),!(cont)https://docs.raku.org/language/unicode_ascii#Other_acceptabl...
Có vẻ những chỉ trích điển hình xuất hiện trước khi người ta hiểu rõ ngôn ngữ vẫn đang được lặp lại. Trước đây là “nhiễu dòng” của Perl, còn giờ là kiểu nói Raku không ngại dùng toán tử Unicode
Nhưng đó là tùy chọn, và tôi đã tự thử dùng để làm cho mã trên màn hình trông gọn và giàu biểu đạt hơn. Nó rất hợp với việc dùng Unicode một cách thận trọng và sáng tạo. Phản ứng ghét sigil cũng khá phổ biến, nhưng vì thích khả năng biểu đạt kiểu “cưa máy đa năng Thụy Sĩ” của Perl nên tôi đã dùng Raku/Perl 6, và Raku có cảm giác như Perl được nâng lên bình phương theo đúng chất Perl. Nó gọn gàng, giàu biểu đạt, và là một đống tính năng khổng lồ chồng lên Perl cổ điển tốt đẹp. Tài liệu cũng tốt, nhưng vẫn cần được chăm chút liên tục; nếu so với tài liệu Perl thì tiêu chuẩn rất cao
Làm vậy chỉ để tiết kiệm vài ký tự thì có vẻ phức tạp và không có nhiều ý nghĩa
Thỉnh thoảng tôi từng tự hỏi một ngôn ngữ lập trình đầy cú pháp đường sẽ trông như thế nào, giờ thì biết rồi
Cảm giác kiểu “kinh khủng mà lạ lùng cuốn hút, không thể rời mắt, cho xem thêm nữa đi”
Phần giới thiệu trên GitHub viết: “slaps roof of [programming language]* this bad boy can fit so much [syntax sugar] into it.” Người đó cũng dùng ngôn ngữ này để thắng kỳ Advent of Code gần nhất: https://github.com/betaveros/noulith
Cũng đáng nhớ rằng Raku ban đầu bắt đầu với tên Perl 6, và nhiều triết lý thiết kế xuất phát từ tư duy kiểu Perl
Nhìn phản ứng ngay với toán tử
xdùng để lặp chuỗi, có vẻ tác giả không rành lịch sử của Perl và Raku lắm; chuyện đó đã tồn tại trong Perl suốt nhiều thập kỷ rồiKhông phải tự nhiên nó được gọi là Perl 6, và gần như cùng một đội Perl đã phát triển nó. Ai từng dùng Perl 5 kha khá sẽ thấy rõ di sản trong Perl6/Raku. Toàn bộ mô hình đối tượng của Raku cũng gần như là một phiên bản mạnh hơn một chút của Moose.pm, module Perl 5 trên CPAN
Thành thật mà nói, cú pháp regex Perl 5/PCRE thật kinh khủng
Lý do nó tồn tại chỉ là vì trong cú pháp regex cũ,
(?từng là lỗi cú pháp, nên có thể tái định nghĩa thành bất cứ thứ gì. Raku là nỗ lực thiết kế một ngôn ngữ regex lành mạnh ngay từ đầu, khi ta đã biết regex cần biểu đạt điều gì. Phương án thay thế là bị mắc kẹt thêm 30 năm nữa với những thứ như(?:this|(?>or that))Đã lâu không đụng đến Perl, nhưng tôi vẫn thường xuyên dùng regex
Theo một nghĩa nào đó thì đúng là gremlin. Tôi thích những công cụ kỳ quặc, phức tạp nhưng giúp tăng năng suất
Tuy nhiên tôi không đồng ý với so sánh “chương trình lớn vs chương trình nhỏ”. Người kém khôn ngoan hơn có thể hiểu đó là ngôn ngữ tệ cho các công việc lớn, nhưng thực tế nó có thể tốt ngang hoặc tốt hơn các ngôn ngữ khác. Vấn đề là, cũng như các ngôn ngữ gremlin khác, muốn dùng tốt thì cần sự khôn ngoan. Ví dụ, người nhầm
$xvới@xgần như không có nếu họ đã dùng đủ nhiều ngôn ngữ tương tự. Sigil khi đọc mã sẽ cho biết ngay kiểu đơn giản của biến, trong Raku là interface, nên lại tiện hơn; cùng một không gian tên biến cũng có thể được dùng hữu ích với các sigil khác nhau. Nó trông kỳ lạ, có vẻ là ký tự thừa và cần biết ý nghĩa, nhưng có thể làm cuộc sống dễ hơn: https://www.perl.com/article/on-sigils/Vấn đề nảy sinh với những người không thật sự biết mình đang làm gì. Với họ, ngôn ngữ gremlin này có thể là cơn ác mộng sống, và họ cần rất nhiều thiết bị bảo hộ như bumper bowling, phao bơi, găng Kevlar, mũ bảo hộ, GPS. Điều đó không có nghĩa là không thể xây nhà chọc trời bằng ngôn ngữ gremlin; chỉ là những non-gremlin dễ gây tai nạn thì không xây được, còn gremlin khôn ngoan thì có thể
Công cụ tồn tại để giúp đội ngũ làm tốt hơn hoặc nhanh hơn những việc họ cần làm. Nếu dùng một công cụ phức tạp không cần thiết để sàng lọc junior hoặc thỏa mãn cái tôi, thì công cụ đó với phần còn lại của tổ chức gần giống một vũ khí hơn là sự trợ giúp