Вам нужен StateT s IO (String, Bool)
, где StateT
предоставляется как Control.Monad.State
(из пакета mtl
), так и Control.Monad.Trans.State
(из пакета transformers
).
Это общее явление называется преобразованием монад, и вы можете прочитать отличное введение в них в Преобразование монад, шаг за шагом.
Существует два подхода к их определению. Один из них находится в пакете transformers
, который использует класс MonadTrans
для их реализации. Второй подход встречается в классе mtl
и использует отдельный класс типов для каждой монады.
Преимущество подхода transformers
заключается в использовании одного класса типов для реализации всего (найдено здесь):
class MonadTrans t where
lift :: Monad m => m a -> t m a
lift
имеет два хороших свойства, которым должен удовлетворять любой экземпляр MonadTrans
:
(lift .) return = return
(lift .) f >=> (lift .) g = (lift .) (f >=> g)
Это замаскированные законы функтора, где (lift .) = fmap
, return = id
и (>=>) = (.)
.
Подход с классами типов mtl
также имеет свои преимущества, и некоторые вещи могут быть чисто решены только с использованием классов типов mtl
, однако недостатком является то, что каждый класс типов mtl
имеет свой собственный набор законов, которые вы должны помнить при реализации. экземпляры для него. Например, класс типов MonadError
(найденный здесь) определяется как:
class Monad m => MonadError e m | m -> e where
throwError :: e -> m a
catchError :: m a -> (e -> m a) -> m a
Этот класс также имеет законы:
m `catchError` throwError = m
(throwError e) `catchError` f = f e
(m `catchError` f) `catchError` g = m `catchError` (\e -> f e `catchError` g)
Это всего лишь замаскированные законы монад, где throwError = return
и catchError = (>>=)
(а законы монад - это замаскированные законы категорий, где return = id
и (>=>) = (.)
).
Для вашей конкретной проблемы способ написания вашей программы будет таким же:
do
-- get the number of games from the command line (already written)
results <- mapM (\game -> playGame game getStdGen) [1..numberOfGames]
... но когда вы пишете свою функцию playGame
, она будет выглядеть так:
-- 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
Есть еще различия между подходами, которые становятся более очевидными, когда вы начинаете складывать более одного преобразователя монад, но я думаю, что это хорошее начало на данный момент.
person
Gabriel Gonzalez
schedule
06.06.2012
RandT
из пакета MonadRandom. - person Daniel Wagner   schedule 06.06.2012