2015-12-17 5 views
1

я описываю следующие вычисления:Выполнить это монадическое вычисление с понятием государства и случайностями

import Control.Monad.State 
import Control.Monad.Identity 
import Control.Monad.Random.Class 

-- * fair coin 
fair :: MonadRandom m => m Bool 
fair = (\p -> p <= 0.5) <$> getRandomR (0,1 :: Double) 

-- * how do i run this 
bar :: (MonadState Bool m, MonadRandom m) => m Bool 
bar = fair >>= \b -> put b >> return b 

И я хотел бы знать, как запустить bar так, что в ожидании, bar вычисляет True половины времени.

+0

Я не понимаю. Что происходит сейчас? –

+3

@Michael: Они написали код с использованием монадных трансформаторов без явно указанного указателя, и они спрашивают, как построить и запустить стек монадных трансформаторов, чтобы получить желаемое поведение. – icktoofay

ответ

3

Короче:

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 
+1

отличный ответ - вы можете немного сократить последнюю часть с помощью 'evalRandIO', как и' evalRandIO $ evalStateT bar False', а также – Carsten

 Смежные вопросы

  • Нет связанных вопросов^_^