Bạn có thể xem xét cách StateT được thực hiện:
newtype StateT s m a = StateT { runStateT :: s -> m (a,s) }
Để kết hợp nhà nước với IO
bạn chỉ cần đặt IO
ở vị trí của m
và nhận được kiểu như mong muốn: s -> IO (a,s)
.
Nếu bạn có lỗi quá, điều này sẽ trở thành một cái gì đó như s -> IO (Either e (a, s))
hoặc s -> IO (Either e a, s)
tùy thuộc vào việc bạn muốn tính toán thất bại có ảnh hưởng đến trạng thái hay không.
Lưu ý rằng bạn không thể tạo s -> Either e (IO (a, s))
một đơn vị
mà không có máy thời gian
.
Cập nhật
Hóa ra bạn không thể làm cho nó một đơn nguyên ngay cả với cỗ máy thời gian.
Để hiển thị tại sao nó là không thể, chúng ta hãy đơn giản hóa đơn nguyên của chúng tôi bằng cách sử dụng ()
thay vì s
đầu tiên: data M e a = M { runM :: Either e (IO a) }
Bây giờ, hãy tưởng tượng chương trình sau đây:
unsafePerformIO :: IO a -> a
unsafePerformIO io = fromLeft $ runM $ do
a <- M $ Right $ io
M $ Left a
Rõ ràng, chức năng này là bất khả thi và do đó, ví dụ đơn lẻ cho M
cũng không thể thực hiện được.
Máy thời gian nào có thể cung cấp cho bạn là khả năng xử lý IO
chính xác như một xử lý State
. Tuy nhiên, tôi không nhận ra rằng Either e (s -> (a, s))
không phải là một đơn nguyên.
Máy thời gian? Bạn có thể cung cấp một số ngữ cảnh? Có một số trò đùa "chơi đùa" hay là một số thuật ngữ khoa học toán học/lập trình nâng cao? – Tarrasch
Không có trò chơi chữ nào ở đây, tôi đang nói về một cỗ máy thời gian thực. Một khi bạn có nó, bạn có thể thực hiện đơn nguyên này. – Rotsor
@Rotsor: cẩn thận để giải thích * tại sao * cho biết máy thời gian là bắt buộc? – ivanm