Tập 33: Effect system V0 — Chi phí lộ mặt

Một trong những lời nói dối phổ biến nhất của các hàm trong lập trình hiện đại là chữ ký của chúng. Một hàm có kiểu fn compute(x: i32) -> i32 hứa hẹn rằng nó chỉ nhận một số và trả về một số. Nhưng thực tế, nó có thể âm thầm ghi file, gửi log qua mạng, hoặc cấp phát một đống bộ nhớ trên heap. Ở hệ thống lớn, những hành vi ẩn này là nguồn gốc của các lỗi giật cục (latency spikes), rò rỉ tài nguyên và hành vi bất định.

Trong DUY, chúng tôi muốn chi phí phải lộ mặt.

fn load_config(path: str) -> Result[Config, IOError]
  effect io, alloc

Sprint 16 giới thiệu Effect System V0. Ý tưởng rất đơn giản: mỗi hàm phải công bố các hiệu ứng ngoại cảnh mà nó gây ra. Nếu một hàm gọi print, nó mang effect io. Nếu nó gọi heap.alloc, nó mang effect alloc. Và nếu một hàm không công bố effect nào, nó là một hàm tinh khiết (pure).

Luật của checker cực kỳ nghiêm khắc: một hàm pure không thể gọi một hàm impure. Nếu anh cố tình lách luật:

fn pure_function() -> i32 {
  print("hello"); // Compile Error!
}

compiler sẽ chặn lại ở cửa kiểm tra effect. Việc phân tích effect thực chất là một bài toán lan truyền tĩnh (static propagation). Khi duyệt qua AST của hàm, checker thu thập tất cả các lời gọi hàm bên trong và so khớp tập hợp effect của chúng với khai báo của hàm cha.

Sự khó khăn và nhược điểm lớn nhất của V0 nằm ở chỗ chúng tôi chưa có polymorphism cho effects (row polymorphism). Điều này nghĩa là các hàm nhận callback (như map hay filter) gặp khó khăn lớn: chúng buộc phải giả định callback có effect tĩnh nào đó, hoặc phải tự mang tất cả các effect có thể. Đây là sự đánh đổi (trade-off) lớn: chúng tôi chấp nhận sự bất tiện của hệ kiểu tĩnh một chiều (effect monomorphism) để đổi lấy một lõi checker đơn giản hoạt động được ngay trong các sprint đầu tiên.

Tôi nhớ mình đã tranh cãi rất nhiều với người đồng nghiệp AI về việc liệu có nên ép buộc khai báo alloc hay không. AI cho rằng việc này quá phiền phức và sẽ làm nản lòng người viết thư viện. Nhưng tôi kiên quyết: "Ở hệ thống nhúng, việc không biết một thư viện có âm thầm cấp phát heap hay không chính là lý do khiến hệ thống crash bất ngờ vì hết RAM. Chi phí phải được phơi bày." Nhìn thấy chữ effect io, alloc trên mọi hàm hệ thống ban đầu có vẻ nặng nề, nhưng nó buộc người thiết kế API phải suy nghĩ kỹ để tìm cách tối ưu hóa code trước khi viết.


Bình luận (0)

Đang tải bình luận...