Haskell类型推断

eyh26e7m  于 2023-08-06  发布在  其他
关注(0)|答案(2)|浏览(109)

为什么下面这段代码在Haskell中编译不了?

g :: (a->a) -> (Char, Bool)
g f = (f 'z', f True)

字符串
据我所知,Haskell尝试推断最一般的类型,然后检查我的类型是否是从Haskell推断的类型推断出来的。在这种情况下,得到了一个不可解的约束系统:f被应用于'z',所以f的类型是Char -> *。但在这种情况下,f被应用于True,因此f必须是Bool -> *类型,但CharBool并不统一。但是,为什么如果我显式地指定了类型,Haskell就不能检查我猜对了吗?毕竟我指定的类型在函数下接近。

p8ekf7hl

p8ekf7hl1#

您的类型签名是

f :: ∀ a . (a -> a) -> (Char, Bool)

字符串
也可以写-和读-forall)。
这意味着,任何调用你的函数的人都可以选择一个类型a,然后使用该类型的示例化。例如,他们可以选择a ~ Stringa ~ Double或其他。
你想表达的是

f :: (∀ a . a -> a) -> (Char, Bool)


也就是说函数f本身必须是多态的。好吧,你可以这么说,它只是需要扩展

{-# LANGUAGE RankNTypes, UnicodeSyntax #-}


在你的源文件上。
但是请注意,只有一个签名为∀ a . a -> a的合理函数,即id

w9apscun

w9apscun2#

请记住,在标准Haskell类型中(不使用需要扩展的更高级功能),类型变量代表函数的调用者做出的选择。该函数的实现必须工作 * 无论 * 为类型变量所做的选择。
所以这个类型签名:

g :: (a->a) -> (Char, Bool)

字符串
表示调用者可以选择他们喜欢的任何类型a,然后提供一个类型为a -> a的函数(供他们选择a),g将给予一个(Char, Bool)对。特别是,调用者可以为a选择Int,然后提供(+1)作为Int -> Int函数参数。
你的实现是这样的:

g f = (f 'z', f True)


此实现与您为g提供的类型不兼容。特别是,当给定(+1)作为Int -> Int函数时,您将尝试计算('z' + 1, True + 1),这显然是病态类型的。
事实上,对于a根本没有可以选择的类型,其中a -> a函数可以应用于Char'z'BoolTrue。您最初的评估是正确的,根本无法为您的函数推断出简单的Haskell类型。
进入更高级的领域,使用扩展名RankNTypes,可以为g的实现给予不同的类型(注意,这种类型永远不会被推断出来,如果你想要它,你必须显式地给出它):

{-# LANGUAGE RankNTypes #-}

g :: (forall a. a -> a) -> (Char, Bool)
g f = (f 'z', f True)


这里,由于forall a.嵌套在参数类型中,调用者 * 不 * 选择a。相反,调用者需要传递一个适用于任何a的多态函数,因为g的实现可以选择a,并且每次使用该函数时都可以做出不同的选择。
多态性的这两种使用模式非常不同,不要将它们混淆。没有扩展,你总是使用“呼叫者选择”系统。

相关问题