2

我目前需要一些大脑训练,我在Haskell 和 Monads上找到了这篇文章

我在练习 7 时遇到问题。随机函数绑定。

为了使问题更易于实验,我将StdGen类型替换为未指定的类型。所以,而不是...

bind :: (a -> StdGen -> (b,StdGen)) -> (StdGen -> (a,StdGen)) -> (StdGen -> (b,StdGen))

我用了...

bind :: (a -> c -> (b,c)) -> (c -> (a,c)) -> (c -> (b,c))

以及实际的功能实现(直接来自练习)

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

还有 2 个随机函数可供试用:

rndf1 :: (Num a, Num b) => a -> b -> (a,b)
rndf1 a s = (a+1,s+1)

rndf2 :: (Num a, Num b) => a -> b -> (a,b)
rndf2 a s = (a+8,s+2)

因此,在 Haskell 编译器(ghci)中,我得到...

:t bind rndf2
bind rndf2 :: (Num a, Num c) => (c -> (a, c)) -> c -> (a, c)

这与rndf2作为第一个参数的绑定 curried 匹配。

但我不明白的是如何...

:t bind rndf2 . rndf1

突然给

bind rndf2 . rndf1 :: (Num a, Num c) => a -> c -> (a, c)

这是我们正在尝试制作的正确类型的作品,因为

bind rndf2 . rndf1

是一个函数:

  1. 采用相同的参数类型rndf1
  2. AND 接受 return fromrndf1并将其作为输入rndf2以返回相同的类型rndf2

rndf1可以采用 2 个参数a -> crndf2返回(a, c),因此它匹配这些函数的组合应具有的类型:

bind rndf2 . rndf1 :: (Num a, Num c) => a -> c -> (a, c)

这与我最初为 bind 提出的幼稚类型不匹配

bind f :: (a -> b -> (c, d)) -> (c, d) -> (e, f)

这里bind神话般地接受一个接受两个参数的函数并产生一个接受一个元组的函数,以便可以将输出rndf1输入rndf2

  1. 为什么绑定函数需要按原样编码
  2. 为什么绑定函数没有天真的类型
4

1 回答 1

1

bind应该有两个功能。您的“初始”类型签名采用一个函数和一个元组并生成一个元组。这并不真正适用于这个问题:rndf1它不是一个元组,它是一个函数。一旦参数被应用到它,只有这样才会rndf1 a s变成一个元组。所以bind需要一个具有两个功能的功能。

将元组通过管道传递到接受两个参数的函数中的神秘力量在于bind.

bind :: (a -> c -> (b,c)) -> (c -> (a,c)) -> (c -> (b,c))
bind f x seed = let (x',seed') = x seed 
                 in f x' seed'

在这个定义中,f和都是函数(如果你重命名为 ,x这可能会更清楚。)xg

let (x',seed') = x seedx seed产生一个元组。在等号的左侧,该元组的各个元素被赋予了新名称,然后在下一行将这些名称传递给函数f

因此,分解表达式可能会有所帮助bind rndf2 . rndf1

请记住,所有函数实际上都只有一个参数。的类型rndf1可以最准确地写为(Num a , Num b) => a -> (b -> (a,b))。这对于理解接下来的内容非常有帮助。

另一件有帮助的事情是考虑如何将这个表达式与参数一起使用。如:(bind rndf2 . rndf1) a s

由于所有函数都有一个参数,因此它们被一一发送到括号内。第一:((bind rndf2 . rndf1) a) s

现在,您可以在没有运算符的情况下重写表达式.(bind rndf2 (rndf1 a)) sa被传递给,rndf1因为它是这样.工作的:点右侧的函数接受输入,然后将其输出传递给左侧的函数。

您会看到 的类型签名rndf1 a与 的第二个参数匹配bindrndf2然后将参数应用(rndf1 a)bind

bind rndf2 (rndf1 a) seed = let (x', seed') = (rndf1 a) seed
                             in rndf2 x' seed'

现在你在括号内有了一个函数,它只接受一个seed参数。所以你可以把s它应用到函数中:

bind rndf2 (rndf1 a) s = let (x', seed') = (rndf1 a) s
                          in rndf2 x' seed'
于 2012-06-27T21:08:24.540 回答