Haskell Monad绑定currying

时间:2012-06-26 18:19:07

标签: haskell monads

我目前需要进行一些大脑训练,我在Haskell and 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)

这匹配curry with rndf2作为第一个参数。

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

:t bind rndf2 . rndf1

突然给出

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

这是我们试图制作的正确的合成类型,因为

bind rndf2 . rndf1

是一个功能:

  1. 采用与rndf1
  2. 相同的参数类型
  3. AND从rndf1获取回报并将其作为rndf2的输入进行管道,以返回与rndf2相同的类型
  4. rndf1可以使用2个参数a -> crndf2返回(a, c),因此它匹配这些函数的组合应具有类型:

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

    这与我最初提出的绑定

    的天真类型不符
    bind f :: (a -> b -> (c, d)) -> (c, d) -> (e, f)
    

    这里bind神秘地采用一个函数,该函数接受两个参数并生成一个带有元组的函数,以便rndf1的输出可以输入rndf2

    1. 为什么绑定函数需要按原样进行编码
    2. 为什么绑定功能没有天真类型

1 个答案:

答案 0 :(得分:0)

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'

在此定义中,fx都是函数(如果您将x重命名为g,可能会更清楚。)

选择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,因为这就是.的工作原理:点右侧的函数接受输入,然后将其输出传递给左侧的函数。 / p>

您会看到rndf1 a的类型签名与bind的第二个参数匹配。然后将参数rndf2(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'