3 điểm bởi GN⁺ 2025-11-04 | 1 bình luận | Chia sẻ qua WhatsApp
  • Bài viết phân tích mã C của trình thông dịch ngôn ngữ K chỉ khoảng 50 dòng do Arthur Whitney tạo ra, đồng thời lý giải phong cách lập trình độc đáo của ông
  • Mã nguồn chứa nhiều cấu trúc thử nghiệm khác với mã C thông thường như cú pháp nén dựa trên macro, mở rộng C phi tiêu chuẩn, và cách dùng đối số ngầm
  • Tác giả tự mình diễn giải ý nghĩa của từng macro và hàm, đồng thời khám phá triết lý của các ngôn ngữ họ APLưu nhược điểm của mật độ mã cao
  • Ưu điểm của đoạn mã là độ ngắn và khả năng cấu thành cao, còn nhược điểm là cú pháp phi tiêu chuẩn và độ dễ đọc suy giảm
  • Kết luận, đoạn mã này được xem là một ví dụ cho thấy tầm quan trọng của tư duy viết mã sau khi đã hiểu trọn vẹn bài toán, hơn là chỉ học cách “viết cho ngắn”

Arthur Whitney và mã của ông

  • Arthur Whitney là nhà khoa học máy tính đã thiết kế ngôn ngữ A, K, Q và các cơ sở dữ liệu kdb, Shakti
    • kdb là cơ sở dữ liệu chuỗi thời gian siêu tốc được dùng trong lĩnh vực tài chính, còn Shakti là phiên bản nhanh hơn, được thiết kế để xử lý tập dữ liệu quy mô 1 nghìn tỷ dòng
  • Các ngôn ngữ của ông là ngôn ngữ dựa trên mảng, chịu ảnh hưởng mạnh từ APL, đề cao tính cô đọng và sức biểu đạt toán học
  • Trọng tâm của bài viết không phải là ứng dụng tài chính, mà là phân tích phong cách rất đặc biệt trong mã C do Whitney viết

Cấu trúc của trình thông dịch K 50 dòng

  • Kho lưu trữ ksimple được công khai có chứa một trình thông dịch C khoảng 50 dòng mà Whitney viết chỉ trong vài ngày
  • Cốt lõi của mã gồm hai tệp a.ha.c, nổi bật với việc rút gọn định nghĩa hàm bằng macrocấu trúc dùng con trỏ như số nguyên
  • Cú pháp typedef char*s,c; định nghĩa s là con trỏ chuỗi và c là kiểu ký tự
  • s Q=(s)128; là ví dụ cho việc dùng con trỏ như số nguyên; trong toàn bộ mã, Q được dùng như một giá trị đặc biệt biểu thị trạng thái lỗi
  • Nhiều cú pháp mở rộng của GCC được dùng, như statement expression dạng ({e;}) và toán tử ?:

Ý nghĩa của các macro và hàm chính

  • #define _(e...) ({e;}) : macro gói nhiều câu lệnh thành một biểu thức
  • #define i(n,e) : cách viết rút gọn cho vòng lặp, biểu diễn vòng for trên một dòng
  • #define Q(e) cùng các biến thể là macro xử lý lỗi; Qr, Qd, Qz lần lượt trả về lỗi rank, domain, và not-yet-implemented
  • Các macro _s, _i, f, F giúp đơn giản hóa khai báo hàm, đồng thời ngầm sử dụng các đối số x, a
  • ax, ix, nx... là các macro phân biệt kiểu dữ liệu và lập chỉ mục; ax dùng để xác định “x có phải atom hay không”
  • f(w,write(1,ax?&x:x,ax?1:strlen(x));x)hàm xuất, in atom như ký tự, còn vector như chuỗi

Cách trình thông dịch hoạt động

  • Hàm m(x) thực hiện cấp phát bộ nhớ và tạo con trỏ kèm thông tin độ dài, với độ dài tối đa của vector là 255 byte
  • Macro g(a,v) hợp nhất xử lý phép toán trên atom/vector, và các hàm như not, sub, At, _A được định nghĩa dựa trên đó
  • Macro G(f,o) tự động sinh hàm toán tử nhị phân, hỗ trợ các phép như <, ==, +, *, &, |
  • cat, rev, cnt, Tak là các hàm thao tác vector; rev dùng hàm ind để tạo chỉ số đảo ngược
  • Hàm e()bộ đánh giá đệ quy, đọc chuỗi từ phải sang trái và xử lý biến một ký tự, số, toán tử
  • main() nhận đầu vào, đánh giá bằng e(), rồi in kết quả theo dạng vòng lặp REPL

Đánh giá về phong cách mã

  • Ưu điểm
    • Tập phép toán nguyên thủy ngắn gọn được cấu thành từ các macro có thể kết hợp
    • Nhờ chiều dài mã ngắn, có thể nắm toàn bộ logic trong một tầm nhìn mà không cần cuộn nhiều
    • Biểu đạt mật độ cao giúp nén cấu trúc logic của mã
  • Nhược điểm
    • Xử lý kiểu thiếu ý nghĩa khi dùng char* như số nguyên
    • Độ dễ đọc giảm do dùng trực tiếp mã ASCII, toán tử ba ngôi phức tạp, và cú pháp phi tiêu chuẩn
    • Đối số ngầmtên biến ngắn khiến khó nắm được chủ ý
  • Yếu tố trung tính
    • Cú pháp dành riêng cho GCC (?:, statement expression) khá thú vị nhưng làm giảm tính khả chuyển
    • Cách dùng đối số ngầm hữu ích trong mã nhỏ, nhưng có thể gây rối trong mã lớn
    • Tên ngắn có thể hiệu quả khi đã quen, nhưng sức truyền đạt ý nghĩa thì yếu

Kết luận và bài học

  • Đoạn mã này không chỉ nói về “cách viết ngắn”, mà cho thấy một lối tư duy viết mã sau khi đã hiểu hoàn toàn bài toán
  • Mã của Whitney là hình thức chuyển một mô hình toán học đã hoàn chỉnh vào mã, tức là “kết quả của việc biểu đạt suy nghĩ bằng code”
  • Tác giả nhìn lại thói quen thường giải quyết bài toán ngay bên trong code của bản thân,
    và nhấn mạnh rằng trong tương lai cần coi trọng hơn mô hình hóa khái niệm và sắp xếp tư duy trước khi viết code
  • Cuối cùng, thí nghiệm này được tổng kết như một trải nghiệm rèn luyện năng lực đọc code, đồng thời khám phá sự cân bằng giữa mật độ mã và độ rõ ràng trong tư duy

Ý tưởng thử nghiệm tiếp theo

  • Đề xuất các bài thực hành mở rộng trình thông dịch:
    • Hỗ trợ vector số thực dấu chấm động
    • Xử lý hơn 255 phần tử
    • Số nhiều chữ số và tên biến nhiều ký tự
    • Literal mảng và bỏ qua khoảng trắng
    • Bổ sung quản lý bộ nhớ và chức năng hiển thị lỗi
    • Hoàn thiện các hàm chưa được triển khai
  • Những mở rộng này có thể trở thành một thử nghiệm nhằm phát triển thành một ngôn ngữ thực sự dùng được trong khi vẫn giữ phong cách mã kiểu Whitney

1 bình luận

 
GN⁺ 2025-11-04
Ý kiến Hacker News
  • Các macro trong đoạn mã này dùng để nén những thao tác chung Tôi đã đọc J Incunabulum trước rồi mới xem đoạn mã này, nên với các lập trình viên quen C mà bắt đầu đọc từ giữa thì phần định nghĩa macro ở đầu có thể gây bối rối Vì các macro được xây chồng lên nhau nên đoạn mã nhanh chóng leo lên chiếc thang trừu tượng hóa Tôi đặc biệt thích macro Iterate (i), vì nó rút gọn những vòng lặp dài dòng xuống còn một ký tự Lý do kiểu mã đặc đặc như thế này khó đọc là vì nó tạo ra rất nhiều lớp trừu tượng chỉ trong vài chục dòng rồi dùng ngay lập tức Vì thế phải đọc thật chậm, từng ký tự một Từ góc nhìn của người từng làm việc với các codebase lớn gồm hàng trăm file mỏng, kiểu nén cực đoan này lại thấy khá mới mẻ

    • Tôi cũng từng có cảm giác tương tự khi đọc Incunabulum, nhưng khi đổi tên biến thành emoji thì mọi thứ lại dễ hiểu hơn hẳn Nhìn vào ảnh mã phiên bản emoji có thể thấy một phần vấn đề không chỉ là mật độ thông tin mà còn là hình dạng ký tự (orthography) Các ngôn ngữ hiện đại thường không cho dùng ký hiệu hay emoji trong định danh, nhưng nếu có được sự phân biệt thị giác như vậy thì việc đọc sẽ dễ hơn nhiều Ngoài ra, đa số editor dùng tô màu theo cú pháp, nhưng tô màu theo token (mỗi định danh có màu riêng) nhiều khi còn hữu ích hơn “hashed syntax highlighting” của Sublime Text là một ví dụ Đổi như vậy xong thì cấu trúc mã hiện ra rất rõ ngay trước mắt
    • Có vẻ như các lập trình viên lại thích codebase khổng lồ hơn Tôi thích kiểu cấu trúc có thể tìm ngay bằng grep *.[ch] mà không cần thư mục con Dự án Java đặc biệt có quá nhiều file nhỏ mà nội dung lại nghèo nàn nên rất khó tìm Có IDE thì đỡ hơn, nhưng tôi không dùng Whitney từng nói trong một cuộc phỏng vấn rằng ông muốn toàn bộ mã nằm trên một trang, và “IDE” của ông là Windows console và Notepad
  • Muốn hiểu mã C của Arthur Whitney thì trước hết phải học các ngôn ngữ họ APL Nếu không thì nó chỉ trông như một kiểu viết C kỳ quái Whitney đang cố dùng C theo cách của APL Phong cách không có khoảng trắng, dùng tên một ký tự và tạo hàm một dòng cũng giống hệt trong APL Nó hơi giống việc một lập trình viên Pascal viết kiểu #define begin { vậy, chỉ là Whitney còn độc đáo hơn nhiều

    • Ngay cả từ góc nhìn của người dùng APL thì cái này vẫn trông kỳ quặc Ngôn ngữ K do Whitney tạo ra có dùng kiểu này, nhưng hàm một dòng thì trong APL truyền thống là không thể Macro, toán tử ba ngôi hay tên biến ngầm định cũng không có trong APL Bản chất của APL là phép toán trên mảng bất biến, còn phong cách C của Whitney thì khác với triết lý đó
    • Về câu “giống như lập trình viên Pascal chuyển sang C rồi viết #define begin {”, có người đùa đáp lại: “À, kiểu Stephen Bourne ấy mà”
    • Ban đầu trông nó giống ngôn ngữ hàm, nhưng rồi nhanh chóng gợi nhớ tới nỗi kinh hoàng của bộ tiền xử lý C
    • Ngay ở phần mở đầu bài viết đã giải thích rằng “C của Whitney được truyền cảm hứng từ APL” rồi Đây là lời phàn nàn rằng có quá nhiều bình luận chỉ tóm tắt lại điều hiển nhiên
    • Học J cũng có thể là lựa chọn ổn Nó dễ tiếp cận hơn APL vì dùng các ký hiệu có thể nhập bằng bàn phím thông thường
  • Khi tìm về Shakti tôi thấy liên kết Wikipedia chuyển hướng sang k.nyc, mà trên trang chỉ có đúng một chữ ‘k’ Xem mã nguồn thì thật sự chỉ có mỗi `k

` Nó giống như phiên bản HTML của chủ nghĩa tối giản kiểu Whitney — bỏ hết mọi thứ không cần thiết, như thể giao phần còn lại cho compiler tự ngầm xử lý

  • k

  • Bài blog này là một bài phân tích xuất sắc, bất kể đánh giá của người đọc về phong cách lập trình của Whitney ra sao Với việc tác giả viết nó chỉ trong 8 tiếng thì độ sâu rất ấn tượng, và phần kết luận đặc biệt đáng nhớ Liên kết bài gốc

  • Nó khiến tôi nhớ đến nỗ lực của Stephen Bourne nhằm biến C thành thứ gì đó giống Algol Nhìn vào ví dụ mac.hví dụ expand.c sẽ thấy cảm giác khá tương đồng

  • Mọi lĩnh vực đều có cái gọi là “best practice”, nhưng thứ đó chỉ thực sự hợp với trường hợp trung bình Trong một số hoàn cảnh cụ thể, đi ngược lại mới có khi là lựa chọn tốt hơn Cuối cùng thì trí tuệ tập thể nên được lấy làm mặc định, nhưng khi bắt đầu tự suy nghĩ thì bạn sẽ thấy những khe hở của nó

    • Vì vậy tôi ghét cụm từ “best practice” Thật ra nó chỉ là một phương án thỏa hiệp ở mức trung bình mà thôi Đó là cuộc trao đổi giữa việc hy sinh hiệu quả và hiệu năng để lấy khả năng bảo trì và tính nhất quán
    • Việc tạo ra sản phẩm tốt và độ dễ đọc hay đường cong học tập của codebase là hai chuyện khác nhau Một bên làm tốt không có nghĩa bên còn lại sẽ tự động đi theo
  • Tôi thích việc bài viết không tỏ ra công kích với đoạn mã mà vẫn giữ được góc nhìn cân bằng Đọc rất thú vị và tôi định sau này sẽ đọc lại

  • Tôi từng thắc mắc liệu kiểu lập trình này có phải là một paradigm cụ thể nào đó không Trong dự án thực tế tôi gần như chưa từng thấy mã như vậy, ngoài vài ngoại lệ kiểu “business card ray tracer” Mã nguồn của ngôn ngữ J do Whitney tạo ra cũng mang phong cách nén cực độ tương tự

    • Đúng vậy, đây là phong cách lập trình rất riêng của Whitney Nó xuất hiện nhất quán trong các interpreter ngôn ngữ mảng của ông, và nổi tiếng vì nhét được toàn bộ phần cài đặt vào chỉ vài trang Cũng có liên kết meta bình luận tổng hợp các thảo luận HN liên quan
    • Trước câu “tôi chưa từng thấy mã như thế này ngoài đời”, có người đáp: “Bạn may mắn đấy” Đây không còn là C nữa mà giống một DSL nội bộ tự tạo bên trên C hơn C chỉ là đích biên dịch đầu tiên mà thôi
    • Nó tương tự các ngôn ngữ họ APL như J và K Chúng dùng ký hiệu ngoài ASCII và có thể nhồi rất nhiều thông tin lên một trang với mật độ cực cao Khi đã quen thì nó cũng có ưu điểm là làm giảm số tầng trừu tượng
    • Cũng có video liên quan đến co-dfns làm theo hướng tiếp cận tương tự Không phải C, nhưng cũng được viết theo phong cách mật độ cao khá giống nhau
    • Có người đùa gọi đây là “OCC (Obfuscated C Code)”
  • Nhìn vào các định nghĩa macro sau

    #define _(e...) ({e;})
    #define x(a,e...) _(s x=a;e)
    #define $(a,b) if(a)b;else
    #define i(n,e) {int $n=n;int i=0;for(;i