gì bạn muốn là StateT s IO (String, Bool)
, nơi StateT
được cung cấp bởi cả hai Control.Monad.State
(từ gói mtl
) và Control.Monad.Trans.State
(từ gói transformers
).
Hiện tượng chung này được gọi là biến áp đơn nguyên và bạn có thể đọc phần giới thiệu tuyệt vời về chúng trong Monad Transformers, Step by Step.
Có hai cách để xác định chúng. Một trong số chúng được tìm thấy trong gói transformers
sử dụng lớp MonadTrans
để triển khai chúng. Cách tiếp cận thứ hai được tìm thấy trong lớp mtl
và sử dụng một loại lớp riêng biệt cho mỗi đơn vị.
Ưu điểm của cách tiếp cận transformers
là việc sử dụng một loại lớp duy nhất để thực hiện tất cả mọi thứ (tìm thấy here):
class MonadTrans t where
lift :: Monad m => m a -> t m a
lift
có hai thuộc tính tốt đẹp mà bất kỳ trường hợp MonadTrans
phải đáp ứng:
(lift .) return = return
(lift .) f >=> (lift .) g = (lift .) (f >=> g)
Đây là các luật functor trong ngụy trang, trong đó (lift .) = fmap
, return = id
và (>=>) = (.)
.
Cách tiếp cận mtl
type-class có lợi ích của nó, quá, và một số điều chỉ được sạch sẽ có thể giải quyết bằng cách sử dụng mtl
kiểu lớp, tuy nhiên nhược điểm là sau đó rằng mỗi mtl
type-class đã thiết lập riêng của pháp luật bạn có để nhớ khi thực hiện các trường hợp cho nó. Ví dụ, loại hạng MonadError
(tìm thấy here) được định nghĩa là:
class Monad m => MonadError e m | m -> e where
throwError :: e -> m a
catchError :: m a -> (e -> m a) -> m a
lớp này đi kèm với pháp luật, quá:
m `catchError` throwError = m
(throwError e) `catchError` f = f e
(m `catchError` f) `catchError` g = m `catchError` (\e -> f e `catchError` g)
Đây chỉ là những luật đơn nguyên trong ngụy trang, nơi throwError = return
và catchError = (>>=)
(và các luật đơn nguyên là luật danh mục trong ngụy trang, trong đó return = id
và (>=>) = (.)
).
Đối với vấn đề cụ thể của bạn, cách bạn sẽ viết chương trình của bạn sẽ là như nhau:
do
-- get the number of games from the command line (already written)
results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames]
... nhưng khi bạn viết hàm playGame
của bạn nó sẽ trông cả hai như:
-- transformers approach :: (Num s) => StateT s IO()
do x <- get
y <- lift $ someIOAction
put $ x + y
-- mtl approach :: (Num s, MonadState s m, MonadIO m) => m()
do x <- get
y <- liftIO $ someIOAction
put $ x + y
Có nhiều sự khác biệt giữa các cách tiếp cận trở nên rõ ràng hơn khi bạn bắt đầu xếp chồng nhiều hơn một biến đơn lẻ, nhưng tôi nghĩ đó là một khởi đầu tốt cho bây giờ.
Bạn cũng có thể thưởng thức 'RandT' từ gói [MonadRandom] (http://hackage.haskell.org/package/MonadRandom). –