为什么随机绑定的类型需要额外的StdGen?

时间:2012-07-26 17:12:39

标签: haskell monads

继续tutorial,在部分更复杂的副作用:随机数我来到这里:

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (StdGen → (b,StdGen))

当“随机函数”的类型(如作者所称)时如下:

a → StdGen -> (b,StdGen)

此外,绑定定义为:

bind f x seed = let (x',seed') = x seed in f x' seed'

问题:为什么绑定有一个额外的StdGen签名的结尾?不应该是:

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (b,StdGen)

我的推理如下:

  1. Bind采用函数f:: a -> StdGen -> (b,StdGen)和“输出”StdGen -> (a,StdGen)
  2. 它将f应用于aStdGen,并返回f所说的任何签名 - 只是{{1} }:

    (b, StdGen)
  3. 即使在绑定实现之后,f::a -> StdGen -> (b,StdGen) 也会应用于f类型的值x'seed',因此它的结果必须是元组!

    StdGen
  4. 我出错的地方?任何帮助赞赏!

    N.B。:对于未来的读者,作者对 bind f x seed = let (x',seed') = x seed in f x' seed' 的定义等同于标准的除了翻译的参数:bind

1 个答案:

答案 0 :(得分:3)

让我们选择你的类型:

bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (b,StdGen)

现在,我第一点就100%与你同在:

  

Bind采用函数f :: a -> StdGen -> (b,StdGen)和“输出”StdGen -> (a,StdGen)

但是你的第二个让我担心:

  

它将f应用于aStdGen

您从哪里获得a类型的值?你从哪里得到StdGen类型的值?

这两个问题的答案是“你没有人撒谎”;但是,由于你StdGen -> (a,StdGen)谎言,如果只有一个StdGen参数,你可以得到两者。这就是额外参数的来源。

现在,稍微高一点的解释。部分问题(我认为)是这些类型的签名有点太混乱,无法轻松阅读。我们需要一些抽象。我们在这里要模拟的是概率分布,我们将其建模为其采样函数。因此,我们可以说a上的分布是一个知道如何从分布中抽样并返回a的函数:

type Dist a = StdGen -> (a, StdGen)

现在,并非所有发行版都如此平坦。例如,伯努利分布是Dist Bool的“类型”,但它也参数化了选择False的概率。我们可以这样写出它的类型:

bernoulli :: Double -> Dist Bool

因此,我们可以将参数化分布建模为返回分布的函数;等价地,我们可以考虑将分布作为参数化分布返回的函数。

现在考虑到这种高级解释,bind的类型变得更具可读性:

bind :: (a -> Dist b) -> (Dist a -> Dist b)

这表示bind是告诉如何首先从a分布进行抽样,然后在a分布采样时使用b作为参数的函数。不仅如此,但是使用这种类型的别名,为bind编写一个没有“额外”参数的类型几乎是不可想象的。