1

教程中继续,在更复杂的副作用部分:随机数我来这个:

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. 它将 the 应用于fand aStdGen并返回签名f所说的任何内容 - 这只是(b, StdGen)

    f::a -> StdGen -> (b,StdGen)
    
  3. 即使遵循绑定实现,f也适用于 valuex'seed'type StdGen,所以它的结果必须是一个元组!

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

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

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

4

1 回答 1

3

让我们看看你的类型:

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

现在,我 100% 同意你的第一点:

Bind 接受一个函数f :: a -> StdGen -> (b,StdGen)和“输出” StdGen -> (a,StdGen)

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

它适用faand StdGen

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

The answer to both of these questions is "you don't have one lying about"; however, since you do have a StdGen -> (a,StdGen) lying about, you could get both if only you had one more StdGen parameter. And that's where the extra parameter comes from.

Now, a slightly higher-level explanation. Part of the problem (I think) is that these type signatures are a bit too cluttered to read comfortably. We need some abstractions. What we're trying to model here are probability distributions, which we're modeling as their sampling functions. So, we can say a distribution over a is a function that knows how to sample from the distribution and return an a:

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

Now, not all distributions are so flat as all that. For example, the Bernoulli distribution is "sort of" a Dist Bool, but it's also parameterized on the probability of choosing False. We can write its type thus:

bernoulli :: Double -> Dist Bool

So, we can model parameterized distributions as functions that return distributions; equivalently, we can think of functions that return distributions as parameterized distributions.

Now with this high-level interpretation in mind, the type of bind becomes much more readable:

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

This says that bind is the function that tells how to first sample from an a distribution, then use that a as a parameter when sampling from the b distribution. Not only that, but with this type alias, it almost becomes unthinkable to write a type for bind that doesn't have the "extra" argument.

于 2012-07-26T17:53:38.173 回答