继续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)
我的推理如下:
f:: a -> StdGen -> (b,StdGen)
和“输出”StdGen -> (a,StdGen)
。它将f
应用于a
和StdGen
,并返回f
所说的任何签名 - 只是{{1} }:
(b, StdGen)
即使在绑定实现之后,f::a -> StdGen -> (b,StdGen)
也会应用于f
类型的值x'
和seed'
,因此它的结果必须是元组!
StdGen
我出错的地方?任何帮助赞赏!
N.B。:对于未来的读者,作者对 bind f x seed = let (x',seed') = x seed in f x' seed'
的定义等同于标准的除了翻译的参数:bind
答案 0 :(得分:3)
让我们选择你的类型:
bind :: (a → StdGen → (b,StdGen)) → (StdGen → (a,StdGen)) → (b,StdGen)
现在,我第一点就100%与你同在:
Bind采用函数
f :: a -> StdGen -> (b,StdGen)
和“输出”StdGen -> (a,StdGen)
。
但是你的第二个让我担心:
它将
f
应用于a
和StdGen
。
您从哪里获得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
编写一个没有“额外”参数的类型几乎是不可想象的。