为什么下面这段代码在Haskell中编译不了?
g :: (a->a) -> (Char, Bool)
g f = (f 'z', f True)
字符串
据我所知,Haskell尝试推断最一般的类型,然后检查我的类型是否是从Haskell推断的类型推断出来的。在这种情况下,得到了一个不可解的约束系统:f被应用于'z',所以f的类型是Char -> *
。但在这种情况下,f被应用于True,因此f必须是Bool -> *
类型,但Char
和Bool
并不统一。但是,为什么如果我显式地指定了类型,Haskell就不能检查我猜对了吗?毕竟我指定的类型在函数下接近。
2条答案
按热度按时间p8ekf7hl1#
您的类型签名是
字符串
(
∀
也可以写-和读-forall
)。这意味着,任何调用你的函数的人都可以选择一个类型
a
,然后使用该类型的示例化。例如,他们可以选择a ~ String
或a ~ Double
或其他。你想表达的是
型
也就是说函数
f
本身必须是多态的。好吧,你可以这么说,它只是需要扩展型
在你的源文件上。
但是请注意,只有一个签名为
∀ a . a -> a
的合理函数,即id
。w9apscun2#
请记住,在标准Haskell类型中(不使用需要扩展的更高级功能),类型变量代表函数的调用者做出的选择。该函数的实现必须工作 * 无论 * 为类型变量所做的选择。
所以这个类型签名:
字符串
表示调用者可以选择他们喜欢的任何类型
a
,然后提供一个类型为a -> a
的函数(供他们选择a
),g
将给予一个(Char, Bool)
对。特别是,调用者可以为a
选择Int
,然后提供(+1)
作为Int -> Int
函数参数。你的实现是这样的:
型
此实现与您为
g
提供的类型不兼容。特别是,当给定(+1)
作为Int -> Int
函数时,您将尝试计算('z' + 1, True + 1)
,这显然是病态类型的。事实上,对于
a
根本没有可以选择的类型,其中a -> a
函数可以应用于Char
'z'
和Bool
True
。您最初的评估是正确的,根本无法为您的函数推断出简单的Haskell类型。进入更高级的领域,使用扩展名
RankNTypes
,可以为g
的实现给予不同的类型(注意,这种类型永远不会被推断出来,如果你想要它,你必须显式地给出它):型
这里,由于
forall a.
嵌套在参数类型中,调用者 * 不 * 选择a
。相反,调用者需要传递一个适用于任何a
的多态函数,因为g
的实现可以选择a
,并且每次使用该函数时都可以做出不同的选择。多态性的这两种使用模式非常不同,不要将它们混淆。没有扩展,你总是使用“呼叫者选择”系统。