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

jca*_*er2 8 haskell typeclass

在Haskell中,我可以定义一个这样的函数:

foo :: Int -> Int
foo n = n + 1
Run Code Online (Sandbox Code Playgroud)

如果我想成为肛门,我可以在最后一行添加类型签名,如下所示:

foo :: Int -> Int
foo n = n + 1 :: Int
Run Code Online (Sandbox Code Playgroud)

我也可以用bar这样的类型类定义一个函数:

class Rel a b where
  aToB :: a -> b

bar :: (Rel a b) => a -> b
bar a = aToB a
Run Code Online (Sandbox Code Playgroud)

但是,如果我在执行中添加类型签名bar(良性更改,或者我认为),我会收到编译错误.

bar :: (Rel a b) => a -> b
bar a = aToB a :: b
Run Code Online (Sandbox Code Playgroud)

这是错误:

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
Run Code Online (Sandbox Code Playgroud)

我认为错误意味着,编译器是不是相信,b在执行bar相同的b在类型签名bar.但是,我不确定为什么会这样.

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

GS *_*ica 6

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

{-# LANGUAGE ScopedTypeVariables #-}
bar :: forall a b . (Rel a b) => a -> b
bar a = aToB a :: b
Run Code Online (Sandbox Code Playgroud)

forall本质上说:" ab在范围上应为全高清",并且ScopedTypeVariables是需要被允许使用此语法.

我想这在语言设计中确实是一个历史性的瑕疵.


bhe*_*ilr 2

您的问题是类型变量的范围。当你写的时候

bar :: (Rel a b) => a -> b
bar a = aToB a :: b
Run Code Online (Sandbox Code Playgroud)

第一行( 的类型声明bar)有自己的类型变量作用域,第二行将 视为与:: b不同的 b Rel a b

如果您启用ScopedTypeVariables扩展并将您的类型声明为

bar :: forall a b. (Rel a b) => a -> b
Run Code Online (Sandbox Code Playgroud)

那么b的范围就扩展到了 的定义bar

  • 我之前已经问过几次这个问题了。据我目前的理解,这只是 Haskell98 中的一个疏忽,没有任何缺点。 (2认同)