使用状态和随机性概念运行此monadic计算

时间:2015-12-17 05:19:40

标签: haskell monads monad-transformers state-monad io-monad

我描述了以下计算:

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一半的时间。

1 个答案:

答案 0 :(得分:3)

简而言之:

evalRandIO $ evalStateT bar False

让我们分开来看看。 bar需要能够将Bool维护为状态,并且能够获得随机值。我们可以用任何一种方式筑巢;在这里,我选择将状态分层随机,但你可以反过来做。

evalStateT有此类签名:

evalStateT :: Monad m => StateT s m a -> s -> m a

用语言来说,如果某些东西需要一个状态分层在其他monad之上,它会实现那个状态部分并根据底层monad给你那个动作。名称的eval部分意味着它将抛出结果状态并仅为您提供值;还有execStateT为您提供结果状态并抛出输出,runStateT为您提供两者的元组。我应该注意到,无论如何,我们必须给它一个初始状态。您的代码不使用初始状态,因此我们也可以使用undefined,但我使用了False

现在我们已经实现了状态位,我们还剩下什么?

ghci> :t evalStateT bar False
evalStateT bar False :: MonadRandom m => m Bool

它想要一个可以给它随机值的monad。好吧,我们有其中一个。 Rand会做到这一点。 Rand也有runevalexec个变体,因为它实际上也是一个状态monad;它拥有某种类型的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的{​​{1}}本身一样。我们怎么用呢?好吧,挖掘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

对我来说足够随机。但是,as Carsten mentions in the comments,有一个更短的方式。由于这是一个常见的用例,ghci> let barIO = getStdRandom . runRand $ evalStateT bar False ghci> barIO True ghci> barIO True ghci> barIO False 模块提供了一个快捷方式Control.Monad.Random,它基本上就是我们上面使用的evalRandIO。这是一个简单的替代,最终得到

getStdRandom . runRand