我是Haskell的新手,所以请事先原谅我。
为什么以下的haskell代码没有编译?
似乎编译器以某种方式未能看到表达式(maxBound :: a)
的类型是a
,其中提供了Enum
实例,而不是某些type variable ‘a0’
是ambiguous
。
class (Enum a, Bounded a) => SafeEnum a where
ssucc :: a -> a
ssucc x = if (fromEnum x) < (fromEnum (maxBound :: a)) then succ x else minBound
spred :: a -> a
spred x = if (fromEnum x) > (fromEnum (minBound :: a)) then pred x else maxBound
Stepik.hs:3:32: error:
• Could not deduce (Enum a0) arising from a use of ‘fromEnum’
from the context: SafeEnum a
bound by the class declaration for ‘SafeEnum’
at Stepik.hs:(1,1)-(6,82)
The type variable ‘a0’ is ambiguous
These potential instances exist:
instance Enum Ordering -- Defined in ‘GHC.Enum’
instance Enum Integer -- Defined in ‘GHC.Enum’
instance Enum () -- Defined in ‘GHC.Enum’
...plus six others
默认情况下,即使类型变量的范围从定义的类到类的方法的类型签名(即,a
中的class SafeEnum a
与a
中的a
相同ssucc :: a -> a
),它们的范围也不是类型签名的范围。方法体的方法,所以在函数maxBound :: a
和ssucc
的体中的表达式spred
中,a
与这些函数的类型签名中的a
无关。
你可以启用ScopedTypeVariables
扩展,如下所示:
{-# LANGUAGE ScopedTypeVariables #-}
之后,类定义将进行类型检查。
请注意,如果使用forall
关键字,此扩展仅适用于“普通”函数声明。因此,在类定义之外,您需要启用此扩展并编写:
ssucc :: forall a. a -> a
ssucc x = ... maxBound :: a ...
或者实际上:
ssucc :: forall a. (Enum a, Bounded a) => a -> a
ssucc x = ... maxBound :: a ...
但是在class
条款中规则是不同的。
有关详细信息,请参阅the GHC docs。
您需要将此行添加到文件的顶部:
{-# LANGUAGE ScopedTypeVariables #-}
如果没有启用此扩展,maxBound :: a
不会引用与班级相同的a
。
实质上,在标准Haskell中,每个类型签名都有自己的类型变量,它们独立于任何其他变量。例如,这段代码
foo :: [a] -> Int
foo xs = length ys
where
ys :: [a]
ys = xs
失败,因为ys :: [a]
真的意味着ys :: [b]
具有独立变量b
,而ys = xs
不会产生[b]
。
启用扩展程序后,此编译:
foo :: forall a . [a] -> Int
foo xs = length ys
where
ys :: [a]
ys = xs
可以说,应该有一个不同的默认值,例如默认情况下,扩展名应为on。或者,当相同的a
被使用两次时,GHC应该暗示将延长开启,因为通常这是问题。