Các ngôn ngữ chức năng, theo định nghĩa, không nên duy trì các biến trạng thái. Tại sao, sau đó, Haskell, Clojure và những người khác cung cấp triển khai bộ nhớ giao dịch phần mềm (STM)? Có mâu thuẫn giữa hai cách tiếp cận?
Các ngôn ngữ chức năng, theo định nghĩa, không nên duy trì các biến trạng thái. Tại sao, sau đó, Haskell, Clojure và những người khác cung cấp triển khai bộ nhớ giao dịch phần mềm (STM)? Có mâu thuẫn giữa hai cách tiếp cận?
Câu trả lời:
Không có gì sai với một ngôn ngữ chức năng duy trì trạng thái có thể thay đổi. Ngay cả các ngôn ngữ chức năng "thuần túy" như Haskell cũng cần duy trì trạng thái để tương tác với thế giới thực. Các ngôn ngữ chức năng "không tinh khiết" như Clojure cho phép các tác dụng phụ có thể bao gồm trạng thái đột biến.
Điểm chính là các ngôn ngữ chức năng không khuyến khích trạng thái có thể thay đổi trừ khi bạn thực sự cần nó . Kiểu chung là lập trình bằng cách sử dụng các hàm thuần túy và dữ liệu không thay đổi và chỉ tương tác với trạng thái có thể thay đổi "không trong sạch" trong các phần cụ thể của mã yêu cầu. Bằng cách đó, bạn có thể giữ phần còn lại của cơ sở mã của mình "thuần túy".
Tôi nghĩ rằng có một số lý do tại sao STM phổ biến hơn trong các ngôn ngữ chức năng:
Cá nhân tôi thích cách tiếp cận của Clojure về việc cho phép tính đột biến, nhưng chỉ trong bối cảnh các "tài liệu tham khảo được quản lý" được kiểm soát chặt chẽ có thể tham gia vào các giao dịch STM. Tất cả mọi thứ khác trong ngôn ngữ là "hoàn toàn chức năng".
;; define two accounts as managed references
(def account-a (ref 100))
(def account-b (ref 100))
;; define a transactional "transfer" function
(defn transfer [ref-1 ref-2 amount]
(dosync
(if (>= @ref-1 amount)
(do
(alter ref-1 - amount)
(alter ref-2 + amount))
(throw (Error. "Insufficient balance!")))))
;; make a stranfer
(transfer account-a account-b 75)
;; inspect the accounts
@account-a
=> 25
@account-b
=> 175
Lưu ý mã trên là hoàn toàn giao dịch và nguyên tử - một người quan sát bên ngoài đọc hai số dư trong một giao dịch khác sẽ luôn thấy trạng thái nguyên tử nhất quán, tức là hai số dư sẽ luôn đạt tới 200. Với đồng thời dựa trên khóa, đây là một vấn đề khó khăn đáng ngạc nhiên để giải quyết trong một hệ thống phức tạp lớn với nhiều thực thể giao dịch.
Để có thêm sự giác ngộ, Rich Hickey thực hiện công việc tuyệt vời để giải thích STM của Clojure trong video này
Theo định nghĩa, các ngôn ngữ chức năng không nên duy trì các biến trạng thái
Định nghĩa của bạn là sai. Ngôn ngữ không thể duy trì trạng thái đơn giản là không thể được sử dụng.
Sự khác biệt giữa các ngôn ngữ chức năng và mệnh lệnh không phải là một trong số chúng có trạng thái còn ngôn ngữ kia thì không. Đó là một cách họ duy trì nhà nước.
Ngôn ngữ bắt buộc có trạng thái lan truyền khắp chương trình.
Các ngôn ngữ chức năng cô lập và duy trì trạng thái rõ ràng thông qua chữ ký loại. Và đó là lý do họ cung cấp các cơ chế quản lý nhà nước tinh vi như STM.
Đôi khi một chương trình yêu cầu trạng thái có thể thay đổi (ví dụ: nội dung cơ sở dữ liệu cho ứng dụng web) và thật tuyệt vời khi có thể sử dụng nó mà không làm mất lợi ích của lập trình chức năng. Trong các ngôn ngữ phi chức năng, trạng thái có thể thay đổi thấm vào mọi thứ. Nếu bạn làm cho nó rõ ràng với một số loại API đặc biệt , thì bạn có thể giới hạn nó trong một khu vực nhỏ có thể xác định được trong khi mọi thứ khác vẫn hoàn toàn hoạt động. Lợi ích của FP bao gồm gỡ lỗi dễ dàng hơn, kiểm tra đơn vị lặp lại, đồng thời không đau và thân thiện với đa lõi / GPU.