添加类型签名会导致编译错误

时间:2014-01-21 22:10:08

标签: haskell typeclass

在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实现中的barb类型签名中的bar相同}。但是,我不确定为什么会这样。

为什么在向函数实现添加类型签名时会出现此编译错误?

2 个答案:

答案 0 :(得分:6)

您实际上需要语言扩展和一些额外语法才能使两个b类型变量相同:

{-# LANGUAGE ScopedTypeVariables #-}
bar :: forall a b . (Rel a b) => a -> b
bar a = aToB a :: b

forall基本上是说“ab应该在整个定义的范围内”,并且需要允许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的定义。