在Haskell中,我可以定义一个这样的函数:
foo :: Int -> Int
foo n = n + 1
如果我想成为肛门,我可以在最后一行添加类型签名,如下所示:
foo :: Int -> Int
foo n = n + 1 :: Int
我还可以根据类型类定义函数bar
:
class Rel a b where
aToB :: a -> b
bar :: (Rel a b) => a -> b
bar a = aToB a
但是,如果我在bar
的实现中添加了一个类型签名(一个良性的改变,或者我认为是这样),我就会遇到编译错误。
bar :: (Rel a b) => a -> b
bar a = aToB a :: b
这是错误:
Could not deduce (Rel a b1) arising from a use of `aToB'
from the context (Rel a b)
bound by the type signature for bar :: Rel a b => a -> b
at q.hs:79:1-23
Possible fix:
add (Rel a b1) to the context of
an expression type signature: b1
or the type signature for bar :: Rel a b => a -> b
In the expression: aToB val :: b
In an equation for `bar': bar val = aToB val :: b
我认为错误意味着编译器不相信b
实现中的bar
与b
类型签名中的bar
相同}。但是,我不确定为什么会这样。
为什么在向函数实现添加类型签名时会出现此编译错误?
答案 0 :(得分:6)
您实际上需要语言扩展和一些额外语法才能使两个b
类型变量相同:
{-# LANGUAGE ScopedTypeVariables #-}
bar :: forall a b . (Rel a b) => a -> b
bar a = aToB a :: b
forall
基本上是说“a
和b
应该在整个定义的范围内”,并且需要允许ScopedTypeVariables
使用此语法。
我想这在语言设计中确实是一个历史性的瑕疵。
答案 1 :(得分:2)
您的问题是类型变量的范围。当你写
bar :: (Rel a b) => a -> b
bar a = aToB a :: b
第一行(bar
的类型声明)具有自己的类型变量范围,在第二行,它将:: b
视为与Rel a b
不同的b。
如果您启用ScopedTypeVariables
扩展程序并将类型声明为
bar :: forall a b. (Rel a b) => a -> b
然后b
的范围扩展到bar
的定义。