2012-07-26 81 views
1

tutorial继续,在第一个更复杂的副作用:随机数我来到这所:为什么随机绑定的类型需要额外的StdGen?

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. 绑定需要一个功能f:: a -> StdGen -> (b,StdGen)和 “输出” StdGen -> (a,StdGen)
  2. 它应用faStdGen,并返回任何的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 . >>=

回答

3

让我们把你的类型:

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

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

绑定功能f :: a -> StdGen -> (b,StdGen)和“输出”StdGen -> (a,StdGen)

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

它应用faStdGen

你从哪里得到了类型为a的值?你从哪里得到StdGen的价值?

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

现在,稍高级别的解释。部分问题(我认为)是这些类型的签名有点太杂乱,无法舒适地阅读。我们需要一些抽象。我们试图在这里建模的是概率分布,我们将其作为抽样函数进行建模。因此,我们可以说在a分布是知道如何从分布采样并返回a功能:

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

现在,并不是所有的分布是如此平坦的一切。例如,伯努利分布是“排序”,但它也参数化了选择False的概率。我们可以这样写它的类型:

bernoulli :: Double -> Dist Bool 

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

有了这个高层次的解释

现在,bind类型变得更可读:

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

这就是说bind是从a分布将告诉您如何第一个样品的功能,然后用即a作为从b分布取样时的参数。不仅如此,而且对于这种类型的别名,编写bind的类型并不具有“额外”参数几乎是不可想象的。

相关问题