- John Carmack chia sẻ quan điểm cá nhân về việc sử dụng biến có thể thay đổi (mutable variable)
- Ông cho biết khi dùng Python, ông đã trở nên lơ là với nguyên tắc “gán một lần” (single assignment), và nói rằng bản thân cần phải tự cảnh giác với điều đó
- Ông nhấn mạnh rằng nên tránh gán lại hoặc cập nhật biến, ngoại trừ các phép tính lặp trong vòng lặp
- Khi tất cả các bước tính toán trung gian đều được giữ lại, điều đó hữu ích khi gỡ lỗi, đồng thời ngăn vấn đề vô tình dùng giá trị trước đó khi di chuyển các khối mã
- Ông giải thích rằng trong C/C++, việc khai báo gần như mọi biến là const ngay tại thời điểm khởi tạo là một thói quen tốt
- Cuối cùng, ông nhấn mạnh rằng ông “ước gì mutable là một từ khóa”, thể hiện mong muốn tính bất biến trở thành mặc định
1 bình luận
Ý kiến trên Hacker News
Sau 2 năm dùng Clojure, tôi nhận ra việc giải thích sự rõ ràng mà tính bất biến mang lại cho các lập trình viên khác thực sự rất khó
Những người đã quen với lối tư duy tạo ra hiệu ứng thông qua thay đổi trạng thái rất khó hiểu được điều này nếu chưa tự mình trải nghiệm
Ví dụ, nếu viết
x = 7; x = x + 3; x = x / 2thì đổi thứ tự vẫn không báo lỗi nhưng kết quả sẽ khácNgược lại, nếu dùng biến mới như
x1,x2thì khi sai thứ tự sẽ phát sinh lỗi, qua đó làm vấn đề lộ rõ hơnCuối cùng, gán một lần duy nhất (single assignment) là cách biểu đạt tường minh những phụ thuộc kiểu này
Dù có giải thích cho đồng nghiệp chưa từng dùng ngôn ngữ hàm rằng tư duy xoay quanh hàm dễ kiểm thử và gọn gàng đến mức nào, họ vẫn khó cảm nhận được
Python khó viết theo phong cách hàm mà vẫn dễ đọc, còn JS thì tôi thấy lại làm tốt hơn
Cuối cùng chỉ những lập trình viên tò mò mới thử các ngôn ngữ như Clojure
Hàm không cần biết trạng thái bên ngoài, và bên ngoài cũng không cần biết bên trong hàm
Không cần biết toàn bộ trạng thái của chương trình vẫn có thể kiểm thử hoặc gỡ lỗi riêng từng hàm
Thật thú vị khi cộng đồng Haskell rốt cuộc lại cố tái phát minh tính khả biến bên trong hệ thống kiểu
Cốt lõi là kiểm soát tác dụng phụ với chi phí thấp nhất có thể
Haskell có rào cản nhập môn cao vì hệ thống kiểu, còn F# thì quá thỏa hiệp nên rốt cuộc lại viết theo cú pháp C#
Nhờ homoiconicity và các cấu trúc dữ liệu mạnh mẽ của Clojure, lần đầu tiên tôi thật sự hiểu rõ khái niệm “làm việc với giá trị”
Có lẽ tôi sẽ không dùng nó trong công việc, nhưng với ai chưa có kinh nghiệm về ngôn ngữ hàm hay Lisp thì tôi rất muốn giới thiệu
Tôi mong biến mặc định là bất biến và mọi thứ đều là biểu thức (expression)
Nhưng thực tế là với tư cách lập trình viên Clojure, tôi đang khổ sở vì cuộc xâm lấn của Python
Giờ thì tôi lại đang khổ sở vì cuộc xâm lấn của TypeScript, nên rất đồng cảm
Cách này thực sự hữu ích để giới hạn phạm vi thay đổi
Không cần cuốn vào các cuộc chiến bộ lạc giữa các ngôn ngữ
Trong thời đại năng suất tăng vọt, những ranh giới đó trở nên vô nghĩa
Tôi khuyên đọc bài Don’t Call Yourself a Programmer
Tôi cố giảm thiểu việc gán lại biến, nhưng đôi khi vẫn dùng variable shadowing
Tôi thích kiểu
result = result.process()vì nó ngắn gọnVí dụ nếu
process()là hàm kiểm tra/kiểm định, thì sẽ khó biết ở thời điểm nào giá trị đã được xử lýVì vậy tốt hơn là phân biệt rõ trạng thái bằng tên gọi
Ví dụ:
result = x |> foo |> bar |> bazhoặc(-> x foo bar baz)result.process()là cái gì vậy, rốt cuộc là result nào và process gì?”Người đọc lại đoạn code sau này sẽ thấy bối rối
Bản thân từ “biến (variable)” cũng luôn khiến tôi thấy lấn cấn
Đã là bất biến thì tại sao lại gọi là variable?
Trong Rust, chỉ khi khai báo
mutthì mới có thể thay đổiNgược lại trong C, muốn tạo hằng số lại phải dùng preprocessor nên khá rối rắm
xcủa một hàm nhận giá trị khác nhau ở mỗi lần gọi, nên bản thân nó là giá trị thay đổiDù không có gán lại thì vẫn có thể gọi là biến
Lập trình đã mang nguyên khái niệm này sang
Hằng số (constant) thì có cùng một giá trị trong mọi lần chạy
Xem thêm Variable (mathematics)
Sẽ rất hay nếu IDE có thể hiển thị trực quan việc biến có bị thay đổi hay không
Ví dụ chỉ cần đánh dấu nhẹ những biến đã bị thay đổi
Nếu dùng
finalcàng nhiều càng tốt thì code sẽ dễ đọc và dễ bảo trì hơnIDE nên cảnh báo, và chỉ cho phép thay đổi khi thực sự cần thiết
Giống như câu chuyện set vs list của Rich Hickey, nên chọn cấu trúc diễn đạt ý nghĩa một cách rõ ràng
Trước đây tôi từng làm một dự án áp dụng rất nghiêm ngặt tính bất biến vì thread safety
Nhờ vậy code trở nên dễ đọc hơn, và cũng dễ lần theo thứ gì có thể thay đổi
Từ đó tôi trở thành fan cuồng nhiệt của tính bất biến
Sau khi làm trong một codebase Haskell lớn rồi quay lại C, tôi lại thấy giá như bất biến là mặc định
constlà chưa đủMuốn thay đổi thì phải dùng con trỏ, còn C++ thì chỉ cần gọi hàm là đối số cũng có thể bị thay đổi nên khá thiếu minh bạch
Tôi đồng ý với ý rằng “gần như mọi biến đều nên được khai báo là const”
Rust xứng đáng được nhắc đến ở đây
Tôi hình dung ra một cú pháp mà mặc định là bất biến, và chỉ cho phép mutable bên trong một block cụ thể
Ví dụ như block
withcủa PythonKhi ra khỏi block thì lại trở về bất biến
Chỉ cần nhìn vào borrow checker của Rust là thấy khái niệm này phức tạp đến mức nào
Có câu “State is the enemy”
Càng nhiều trạng thái thì số điều kiện phải kiểm thử càng tăng theo cấp số nhân
Tính bất biến là cách ngăn bùng nổ trạng thái như vậy
Separation of Church and State