为什么下面的代码不能在 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 根本无法检查我的猜测是否正确?毕竟我指定的类型在函数下接近。
lef*_*out 10
您的类型签名是简写
\nf :: \xe2\x88\x80 a . (a -> a) -> (Char, Bool)\n(\xe2\x88\x80也可以写为 \xe2\x80\x93 并读为 \xe2\x80\x93forall)。
这意味着,任何调用您的函数的人都可以选择一个类型a,然后使用该类型的实例化。例如,他们可以选择a ~ String或a ~ Double其他。
你似乎想表达的是
\nf :: (\xe2\x88\x80 a . a -> a) -> (Char, Bool)\n说函数f本身必须是多态的。好吧,你可以这么说,它只需要扩展
{-# LANGUAGE RankNTypes, UnicodeSyntax #-}\n在源文件的顶部。
\n但请注意,只有一个带有签名的合理函数\xe2\x88\x80 a . a -> a,即id.
请记住,在标准 Haskell 类型中(不使用需要扩展的更高级功能),类型变量代表函数调用者做出的选择。无论对类型变量做出什么选择,函数的实现都必须起作用。
所以这个类型签名:
g :: (a->a) -> (Char, Bool)
表示调用者可以选择a他们喜欢的任何类型,然后提供一个具有类型的函数a -> a(供他们选择a),并g返回一(Char, Bool)对。Int特别是,对于调用者来说,选择fora然后(+1)作为函数参数提供是有效的Int -> Int。
你的实现是这样的:
g f = (f 'z', f True)
此实现与您为 提供的类型不兼容g。特别是,当(+1)作为Int -> Int函数给出时,您将尝试计算('z' + 1, True + 1),这显然是错误类型的。
事实上,根本没有任何类型可以选择将a函数a -> a同时应用于Char 'z'和Bool True。您的初步评估是正确的,根本无法为您的函数推断出简单的 Haskell 类型。
进入更高级的领域,通过扩展,RankNTypes可以为您的实现提供不同的类型g(请注意,这种类型永远不会被推断,如果您想要的话,您必须明确地给出它):
{-# LANGUAGE RankNTypes #-}
g :: (forall a. a -> a) -> (Char, Bool)
g f = (f 'z', f True)
在这里,由于forall a.嵌套在参数类型中,调用者无法选择a。相反,调用者需要传递一个适用于any的多态函数a,因为实现g可以选择a,并且每次使用该函数时都可以做出不同的选择。
这两种多态性的使用模式非常不同,重要的是不要将它们混淆。如果没有分机,您将始终使用“呼叫者可以选择”系统。