Tôi đang cố gắng làm theo lời khuyên được đưa ra trong Combine state with IO actions để xây dựng một AppState cùng với một đơn nguyên IO. Những gì tôi nhận được là:Làm cách nào để thực thi một đơn vị StateT cùng với IO?
module Main where
import Control.Monad.State
import Control.Monad.Trans
data ST = ST [Integer] deriving (Show)
type AppState = StateT ST IO
new = ST []
append :: Integer -> State ST()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: State ST Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
script = do
append 5
append 10
append 15
sumST
myMain :: AppState()
myMain = do
liftIO $ putStrLn "myMain start"
let (res, st) = runState script new
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain (ST [15])
Có một số phần trong số này tôi không nhận được. Điều này làm tôi bực bội khi tôi có script
và myMain
vàmain
. Nó cũng làm phiền tôi rằng tôi phải thực hiện runState
trong vòng myMain
và tôi phải cấp trạng thái ban đầu vào runStateT
trong chức năng chính của mình. Tôi muốn có "kịch bản" của tôi, vì vậy để nói, trực tiếp trong hàm myMain vì toàn bộ điểm của myMain là để có thể chạy chắp thêm và tổng hợp trực tiếp trong myMain và ngay bên cạnh các thao tác in. Tôi nghĩ rằng tôi sẽ có thể làm được điều này, thay vì:
myMain :: AppState()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
r <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runState myMain
Tôi đã nghĩ rằng điểm của máy biến áp đơn nguyên được vì vậy tôi có thể thực hiện các hoạt động đơn nguyên nhà nước của tôi trong một hàm (như trên) và nhấc hoạt động IO vào chức năng đó. Cách thích hợp để thiết lập tất cả những điều này để tôi có thể loại bỏ một trong các lớp của hướng dẫn là gì?
Ngoài giải pháp của Daniel (mà tôi đã gắn cờ giải pháp), tôi cũng tìm thấy một vài biến thể cũng có thể làm sáng tỏ tình hình. Đầu tiên, việc thực hiện cuối cùng của myMain và chính:
myMain :: AppState()
myMain = do
liftIO $ putStrLn "myMain start"
append 5
append 10
append 15
res <- sumST
liftIO $ putStrLn $ show res
liftIO $ putStrLn "myMain stop"
main = runStateT myMain new
Bây giờ, triển khai khác nhau của append và sumST, ngoài Daniel:
append :: Integer -> AppState()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: AppState Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
và (lưu ý rằng chỉ có loại thay đổi tuyên bố, trong thực tế bạn có thể bỏ qua việc kê khai loại hoàn toàn!)
append :: MonadState ST m => Integer -> m()
append v = state $ \(ST lst) -> ((), ST (lst ++ [v]))
sumST :: MonadState ST m => m Integer
sumST = state $ \(ST lst) -> (sum lst, ST lst)
Nó xảy ra với tôi rằng các đơn nguyên AppState/StateT là không giống như cơ bản Nhà nước monad, và tôi đã được mã hóa cả hai sumST và phụ thêm cho nhà nước monad. Trong một nghĩa nào đó, họ cũng phải được nâng lên thành đơn vị StateT, mặc dù cách suy nghĩ đúng đắn là họ phải là chạy trong đơn nguyên (do đó, runState script new
).
Tôi không chắc mình đã hoàn thành nó, nhưng tôi sẽ làm việc với nó một lúc, đọc mã MonadState và viết điều gì đó về điều này khi nó hoạt động trong đầu tôi.
Ah, 'state' có nhiều hình dạng hơn tôi đã giả định, bởi vì tôi đã có nó trong đầu vì lý do nào đó hàm này là lời xin lỗi vì không xuất một hàm tạo' State' nữa. TIL! –