Tôi sẽ trả lời câu hỏi thứ hai của bạn đầu tiên. Có nhiều cách để xử lý trạng thái có thể thay đổi trong Haskell (và các ngôn ngữ FP khác). Trước hết, Haskell hỗ trợ trạng thái có thể thay đổi trong IO, thông qua các cấu trúc IORef
và mvar
. Sử dụng chúng sẽ cảm thấy rất quen thuộc với các lập trình viên từ các ngôn ngữ bắt buộc. Ngoài ra còn có các phiên bản chuyên biệt như STRef
và TMVar
, cũng như các mảng có thể thay đổi, con trỏ và nhiều dữ liệu có thể thay đổi khác. Hạn chế lớn nhất là chúng thường chỉ có sẵn trong IO hoặc một đơn chuyên biệt hơn.
Cách phổ biến nhất để mô phỏng trạng thái bằng ngôn ngữ hàm là chuyển trạng thái rõ ràng làm đối số hàm và giá trị trả về. Ví dụ:
randomGen :: Seed -> (Int, Seed)
Đây randomGen
lấy tham số hạt giống và trả về một hạt giống mới. Mỗi khi bạn gọi nó, bạn cần phải theo dõi hạt giống cho lần lặp tiếp theo. Kỹ thuật này luôn luôn có sẵn cho nhà nước đi qua, nhưng nó nhanh chóng được tẻ nhạt.
Có lẽ cách tiếp cận Haskell phổ biến nhất là sử dụng một đơn nguyên để gói gọn trạng thái này. Chúng tôi có thể thay thế randomGen
với điều này:
-- a Random monad is simply a Seed value as state
type Random a = State Seed a
randomGen2 :: Random Int
randomGen2 = do
seed <- get
let (x,seed') = randomGen seed
put seed'
return x
Bây giờ bất kỳ chức năng mà cần một PRNG có thể chạy trong đơn nguyên ngẫu nhiên để yêu cầu chúng khi cần thiết. Bạn chỉ cần cung cấp một trạng thái ban đầu và tính toán.
runRandomComputation :: Random a -> Seed -> a
runRandomComputation = evalState
(lưu ý có các hàm rút ngắn đáng kể định nghĩa ngẫu nhiênGen2; tôi chọn phiên bản rõ ràng nhất).
Nếu tính toán ngẫu nhiên của bạn cũng cần quyền truy cập vào IO
, thì bạn sử dụng phiên bản biến áp đơn nguyên của Nhà nước, StateT
.
Lưu ý đặc biệt là ST
đơn nguyên, về cơ bản cung cấp cơ chế đóng gói các đột biến IO cụ thể cách xa phần còn lại của IO. ST monad cung cấp các STREF, là một tham chiếu có thể thay đổi được với dữ liệu và cũng có thể có các mảng có thể thay đổi được.Sử dụng ST, có thể xác định những thông tin như sau:
randomList :: Seed -> [Int]
trong đó [Int] là một danh sách vô hạn các số ngẫu nhiên (nó sẽ chu kỳ cuối cùng tùy thuộc vào PSRG) của bạn từ hạt giống ban đầu mà bạn cung cấp.
Cuối cùng, có Functional Reactive Programming. Có lẽ các thư viện nổi bật nhất hiện nay là Yampa và Reactive, nhưng những thư viện khác cũng đáng xem. Có một số cách tiếp cận đến trạng thái có thể thay đổi trong các triển khai khác nhau của FRP; từ việc sử dụng chúng một cách nhẹ nhàng, chúng thường có vẻ tương tự như một khái niệm về khung tín hiệu như trong QT hoặc Gtk + (ví dụ như thêm người nghe cho các sự kiện).
Bây giờ, cho câu hỏi đầu tiên. Đối với tôi, lợi thế lớn nhất là trạng thái có thể thay đổi được tách biệt với mã khác ở cấp loại. Điều này có nghĩa là mã không thể vô tình sửa đổi trạng thái trừ khi nó được đề cập rõ ràng trong chữ ký loại. Nó cũng cho phép kiểm soát rất tốt trạng thái chỉ đọc so với trạng thái có thể thay đổi (Reader monad vs. State monad). Tôi tìm thấy nó rất hữu ích để cấu trúc mã của tôi theo cách này, và nó rất hữu ích để có thể nói chỉ từ chữ ký loại nếu một chức năng có thể được đột biến nhà nước bất ngờ.
Cá nhân tôi không thực sự có bất kỳ đặt chỗ nào về việc sử dụng trạng thái có thể thay đổi trong Haskell. Khó khăn lớn nhất là nó có thể tẻ nhạt để thêm trạng thái vào cái gì đó không cần nó trước đây, nhưng điều tương tự sẽ tẻ nhạt trong các ngôn ngữ khác mà tôi đã sử dụng cho các nhiệm vụ tương tự (C#, Python).
http://www.haskell.org/all_about_monads/html/statemonad.html –
Trạng thái xử lý bằng ngôn ngữ chức năng liên quan đến việc chuyển trạng thái xung quanh các chức năng. Monads đơn giản hóa điều này. – tylermac
@tylermac tôi không bao giờ có thể hiểu được monads, tốt, tôi không thể nói rằng tôi ngu ngốc, ít nhất tôi là BS trong CS, nhưng monads ... bạn có biết hướng dẫn tốt? – Andrey