【问题标题】:Why is the type of bind for random takes an extra StdGen?为什么随机绑定的类型需要额外的 StdGen?
【发布时间】:2012-07-26 17:12:38
【问题描述】:

继续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 的签名所表示的任何内容——即(b, StdGen)

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

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

我哪里出错了?任何帮助表示赞赏!

注意:对于未来的读者,作者对bind 的定义与标准定义相同,只是参数翻转了:flip . >>=

【问题讨论】:

    标签: haskell monads


    【解决方案1】:

    让我们输入你的类型:

    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 分布中采样,然后在从b 分布中采样时使用a 作为参数。不仅如此,有了这个类型别名,为bind 编写一个没有“额外”参数的类型几乎是不可想象的。

    【讨论】:

      猜你喜欢
      • 2015-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-30
      • 2012-01-18
      相关资源
      最近更新 更多