快速回答
如何使用 aData.Random.RVar
里面的 a Control.Monad.MonadRandom
?
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Monad.Random as CMR
import Data.Random as DR
import Data.Word (Word32)
gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) (getRandom :: m Word32)
return r
解释
实际上,您希望在具有相似语义的形式不同的 Monad 中运行 Monad。
Data.Random.MonadRandom
并且Control.Monad.Random
在形式上是不同的,因为它们在不同的地方独立定义,并且没有一个是另一个的实例(没有instance DR.MonadRandom m => CMR.MonadRandom m
或相反)。
- Monad 具有相似的语义,因为它们都提供来自某个随机源的随机数,因此期望我们可以以某种方式组合它们是有意义的。
假设您在Control.Monad.Random
接口中有一些代码:
import Control.Monad.Random as CMR
gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
r <- getRandomR (0, 100)
return r
我们可以像这样运行它evalRand gimmeRandom StdGen
,这给了我们一个Int
.
现在getRandomR
,您想使用由Data.Random
.
对于此示例,我们将尝试替换getRandomR (0, 100)
为uniform 0 100 :: RVar Int
。我们如何在我们的环境中Int
摆脱它?RVar Int
CMR.MonadRandom
我们想要运行RVar
monad,正如语义所暗示的那样,我们可能必须为其提供一个随机数源。我们正在为 CMR寻找像evalRand这样的单子转义函数。这些转义函数有 type m a -> someStuffNeededToRunTheMonad -> a
。
在有关 RVar的文档中,有一个示例:
-- In a monad, using a RandomSource:
runRVar (uniform 1 100) DevRandom :: IO Int
让我们检查一下runRVar
:
runRVar :: RandomSource m s => RVar a -> s -> m a
是的,这是一种转义函数:给定一个RVar
随机数的和源,它返回我们RVar
自己的 monad 内部的随机结果m
。然而,这需要有一个instance RandomSource m s
表示它s
是我们的 monad 的随机源m
。让我们寻找那个例子。
我们的 monad 是什么m
?我们想运行RVar
in gimmeRandom
,所以 monad 是CMR.MonadRandom m => m
(所有实现的 monad CMR.MonadRandom
)。随机性来源是s
什么?还没有头绪。让我们查看文档中存在哪些RandomSource
实例:
RandomSource IO DevRandom
...
Monad m0 => RandomSource m0 (m0 Word32)
Monad m0 => RandomSource m0 (m0 Word64)
...
啊哈!这表示任何monadm0
都是 的实例RandomSource
以及来自该 monad 的值(例如m0 Word32
)。这当然也适用于我们的 monad CMR.MonadRandom
。我们还可以看到s
, m0 Word32
, 一定是随机源产生的随机值。
我们应该传入什么作为s
in runRVar (uniform 0 100) s
?在我们的monad中生成随机数的东西,类型为CMR.MonadRandom m => m Word32
. CMR
生成任意事物的功能是什么,例如 some Word32
?随机获取。所以基本上我们想写:
gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) getRandom
return r
嗯,那不编译:
Could not deduce (RandomSource m (m0 a0))
arising from a use of `runRVar'
from the context (CMR.MonadRandom m)
bound by the type signature for
gimmeRandom :: CMR.MonadRandom m => m Int
RandomSource m (m0 a0)
? 这很奇怪,them
和 them0
似乎被编译器识别为不同的单子;我们希望它们相同,如RandomSource m0 (m0 Word64)
.
让我们把完整的签名放到那个地方:
r <- runRVar (uniform 0 100) (getRandom :: CMR.MonadRandom m => m Word32)
还是同样的错误。这是因为m
在那个类型签名中,实际上是任何monad 实现CMR.MonadRandom
,不一定是MonadRandom
在我们的gimmeRandom
类型签名中。
(这与 lambda 术语中的阴影概念相同,其中(\x -> (\x -> f x))
inner\x
是 in 中使用的;f x
或者在一阶逻辑中如,作为外部的一个;或者实际上在任何其他编程语言中,在内部范围内具有变量隐藏/阴影——只是这里是类型变量阴影)。∀x . F(x) → ∀x . G(x)
x
G(x)
∀x
所以我们唯一要做的就是告诉编译器,在getRandom
调用中,我们不希望它用于 any MonadRandom
,而正是MonadRandom m
我们在gimmeRandom
类型签名中拥有的那个。
我们可以使用ScopedTypeVariables
扩展来做到这一点:
{-# LANGUAGE ScopedTypeVariables #-}
[...]
gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
r <- runRVar (uniform 0 100) (getRandom :: m Word32)
return r
这使得m
ingetRandom :: m ...
将完全 CMR.MonadRandom m
从顶级类型签名中选择。
这确实编译并解决了问题:我们可以使用接口Data.Random
在代码中使用分布。MonadRandom
我们可以很容易地用uniform
另一个发行版替换。
总而言之,我们有
- 确定我们使用来自不同包但具有相同/重叠语义的两个不同 monad
- 找到了如何运行/转义我们想要在我们自己的内部使用的 monad (with
runRVar
)
- 通过查看类型类限制和为这些限制提供的实例,找出传递给转义函数的内容
- 编写了正确的代码 (
runRVar (uniform 0 100) getRandom
)
- 通过说明
getRandom
应该使用哪个精确的 monad 来编译它。
If you are wondering why we chose Word32
somewhat arbitrarily from the instances we can pick from, we just have to give the randomness source in some form, and Word32 is one of the things Data.Random
takes as input for generating other random stuff.