显式类型签名的多态类型

3

我正在审阅《Haskell函数式编程艺术》,但第356页的类型签名让我感到困惑。

这里有一个简单的例子:

succeed :: b -> Parse a b
succeed val inp = [( val, inp )]

如何理解b -> Parse a b呢?
succeed Int -> Int
succeed a = a

并且,

succeed Int -> Int -> Int
succeed a b = a + b

你所接受的参数数量必须在类型声明中正确吗?如果你的类型声明只有一个类型变量:succeed :: b -> Parse a b 应该是这样的,接受一个类型为 a 的变量,并返回一个类型为 Parse a b 的值,而不是接受两个变量...那么 inp 在哪里被允许?

2个回答

8
因为Parse在其扩展中是与->同义词。例如:
type Foo = Int -> Int

succeed :: Int -> Foo
succeed a b = a + b

我有点茫然地看了一会儿,-> 显式同义词的提及确实很有帮助。我现在明白你的意思了,这似乎使类型系统的清晰度变得更加模糊 - 你或其他人能否对此发表评论? - Evan Carroll
2
@Evan:使用这种快捷方式编写succeed的类型可以明确你可能想要将该函数应用于两个参数。实际上,你只需要将它应用于一个参数,这样它就会生成一个解析器(也就是函数)。它可以仅在左侧使用一个参数进行定义,如下所示:succeed val = \inp -> [(val, inp)],即接受一个名为val的参数,并生成一个parser(也就是函数),该函数接受inp并生成那个列表。实际上,解析器就是一个函数。因为您可以轻松地组合函数,所以可以轻松地组合以此风格编写的解析器。 - R. Martinho Fernandes
我希望这本书能够更明确地说明正在发生的事情。 - Evan Carroll

1
在Haskell中,所有函数都接受一个参数并产生一个结果。当有一个类型 a -> b -> c 时,你拥有的是和 a -> (b -> c) 是相同的。因此,代替一个带有两个参数的函数,实际上你有了一个函数,当给定一个参数时会产生另一个函数,该函数将接受第二个参数并产生最终结果。
当你将一个函数应用于两个参数,例如 f x y, 等同于 (f x) y, 你实际上是将从 f x 得到的函数应用于第二个参数 y。由此你可以免费得到 "部分应用"。
在Prelude中有一对名为 curryuncurry 的函数,它们让你在直接使用一对作为参数的两个函数和使用这种风格编写的函数之间进行转换。
uncurry :: (a -> b -> c) -> ((a,b) -> c)
curry :: ((a,b) -> c) -> (a -> b -> c)

f  :: a -> b -> c
f = ...
f' :: (a,b) -> c
f' = uncurry f

我不知道如何定义Parse,但如果你将其定义为函数类型的同义词(这在解析器中很常见),你就会得到那种行为。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接