Короче:
evalRandIO $ evalStateT bar False
Давайте забрать этот врозь. bar
должен поддерживать Bool
как состояние и получать случайные значения. Мы могли бы вложить ее в любом случае; здесь я решил сложить состояние на случайном, но вы могли бы сделать это наоборот.
evalStateT
имеет этот тип подписи:
evalStateT :: Monad m => StateT s m a -> s -> m a
В словах, учитывая то, что нужно состояние слоистые на вершине какой-либо другой монады, он реализует эту государственную часть и дает вам, что действия с точки зрения основной монады. Часть имени eval
означает, что он выкинет полученное состояние и даст вам только значение; есть также execStateT
, который дает вам результирующее состояние и выдает выход, и runStateT
, что дает вам кортеж обоих. Я должен отметить, что в любом случае мы должны дать ему начальное состояние. Ваш код не использует начальное состояние, поэтому мы могли бы использовать undefined
, но я использовал False
для этого.
Итак, теперь, когда мы реализовали бит состояния, что у нас осталось?
ghci> :t evalStateT bar False
evalStateT bar False :: MonadRandom m => m Bool
Он хочет монаду, которая может дать ему случайные значения. Ну, у нас есть один из них. Rand
сделают это. Rand
тоже имеет run
, eval
и exec
вариантов, так как на самом деле это государственная монада; он имеет значение некоторого типа класса RandomGen
под обложками. Здесь мы не делаем хотим отбросить состояние в конце, и мы делаем тоже хотим сохранить результат, поэтому мы используем вариант run
. Ну, что у нас сейчас?
ghci> :t runRand (evalStateT bar False)
runRand (evalStateT bar False) :: RandomGen g => g -> (Bool, g)
Для сравнения:
ghci> :t random
random :: (RandomGen g, Random a) => g -> (a, g)
Так что теперь мы имеем простую функцию, которая принимает состояние генератора случайных чисел и выплевывает пару с результатом и новым состоянием, так же как и сама random
от System.Random
. Как мы можем это использовать? Ну, рытье через System.Random
модуль, getStdRandom
выглядит полезным:
getStdRandom :: (StdGen -> (a, StdGen)) -> IO a
Он принимает функцию, как тот, который мы имеем, и превращает его в IO
действия. (Это имеет смысл, что это будет IO
, оно принимает некоторое глобальное состояние (а именно, стандартный генератор случайных чисел) и обновляет его.) Что мы имеем после этого?
ghci> :t getStdRandom . runRand $ evalStateT bar False
getStdRandom . runRand $ evalStateT bar False :: IO Bool
Именно то, что мы ожидаем: IO
, что дает Bool
.Запустите его несколько раз:
ghci> let barIO = getStdRandom . runRand $ evalStateT bar False
ghci> barIO
True
ghci> barIO
True
ghci> barIO
False
Вполне достаточно для меня. Однако, as Carsten mentions in the comments, есть более короткий путь. Поскольку это такой общий прецедент, модуль Control.Monad.Random
обеспечивает ярлык, evalRandIO
, который по существу является getStdRandom . runRand
, который мы использовали выше. Это простая замена в конечном итоге с
evalRandIO $ evalStateT bar False
Я не понимаю. Что происходит сейчас? –
@Michael: Они написали код с использованием монадных трансформаторов без явно указанного указателя, и они спрашивают, как построить и запустить стек монадных трансформаторов, чтобы получить желаемое поведение. – icktoofay