这是你的功能:
GHCi> let foo = \(a,b) -> Just(a,(b,a+b))
虽然我们当然可以在脑海中将其解释为两个参数的函数,但就 Haskell 而言,它只需要一个参数...
foo :: Num t => (t, t) -> Maybe (t, (t, t))
...(t, t) 类型的 pair 用于 Num 类中的某些 t。那么,在你的例子中,unfoldr 的签名中的b 被Num t => (t, t) 替换,a 被Num t => t 替换,导致:
Num t => ((t, t) -> Maybe (t, (t, t))) -> (t, t) -> [t]
一些额外的证据:
GHCi> :t unfoldr (\(a,b) -> Just (a,(b,a+b)))
unfoldr (\(a,b) -> Just (a,(b,a+b))) :: Num a => (a, a) -> [a]
回答您的直接问题后,这里有一点题外话。将两个参数传递给 Haskell 函数的另一种方法是将它们分开传递,而不是成对传递:
GHCi> let foo2 a b = Just(a,(b,a+b))
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
显然,如果你身边还有foo,你甚至不需要重写实现:
GHCi> let foo2 a b = foo (a, b)
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
事实上,这种转换——在行话中称为currying——是总是可能的,甚至还有一个函数可以做到这一点...... p>
GHCi> let foo2 = curry foo
GHCi> :t foo2
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
...以及另一个相反的方向:
GHCi> :t uncurry foo2
uncurry foo2 :: Num t => (t, t) -> Maybe (t, (t, t))
然而,令人惊讶的是,即使foo2 看起来比foo 更像是两个参数的函数,它仍然是一个参数的函数!诀窍是像foo2这样的签名...
foo2 :: Num t => t -> t -> Maybe (t, (t, t))
... 有一些省略的可选括号。省略隐藏了函数箭头-> 是右结合的事实。如果我们添加它们,我们会得到:
foo2 :: Num t => t -> (t -> Maybe (t, (t, t)))
也就是说,foo2 接受一个参数(即我们的第一个参数)并返回另一个函数,该函数也接受一个参数(即 我们的第二个参数)。
因此,主要的收获是所有 Haskell 函数都只接受一个参数。幸运的是,编写像接受两个参数一样工作的函数非常容易。通常情况下,这是通过以 curried 方式编写函数来完成的(即,作为返回另一个函数的 foo2 之类的函数),但有时需要或方便成对传递多个值,因为您在你的例子中必须这样做。