编译器不会为多态常量值选择类型类
Compiler doesn't pick up typeclass for the polymorphic constant value
我是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
您需要将此行添加到文件的顶部:
{-# LANGUAGE ScopedTypeVariables #-}
如果不启用此扩展,maxBound :: a
与 class 中引用的 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
可以说,应该有不同的默认值,例如扩展应该默认打开。或者,当相同的 a
被使用两次时,GHC 应该提示打开扩展,因为这通常是问题所在。
默认情况下,即使类型变量的范围从 class 定义到 class 方法的类型签名(即 a
中的 [=14] =] 与 ssucc :: a -> a
中的 a
相同 a
),它们 不是 从方法的类型签名到方法主体的范围,因此在函数 ssucc
和 spred
的主体中的表达式 maxBound :: a
中, a
与类型签名中的 a
无关这些功能。
您可以启用 ScopedTypeVariables
扩展程序,如下所示:
{-# LANGUAGE ScopedTypeVariables #-}
之后 class 定义将进行类型检查。
请注意,如果您使用 forall
关键字,此扩展仅适用于 "normal" 函数声明。因此,在 class 定义之外,您需要启用此扩展 和 写入:
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。
我是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
您需要将此行添加到文件的顶部:
{-# LANGUAGE ScopedTypeVariables #-}
如果不启用此扩展,maxBound :: a
与 class 中引用的 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
可以说,应该有不同的默认值,例如扩展应该默认打开。或者,当相同的 a
被使用两次时,GHC 应该提示打开扩展,因为这通常是问题所在。
默认情况下,即使类型变量的范围从 class 定义到 class 方法的类型签名(即 a
中的 [=14] =] 与 ssucc :: a -> a
中的 a
相同 a
),它们 不是 从方法的类型签名到方法主体的范围,因此在函数 ssucc
和 spred
的主体中的表达式 maxBound :: a
中, a
与类型签名中的 a
无关这些功能。
您可以启用 ScopedTypeVariables
扩展程序,如下所示:
{-# LANGUAGE ScopedTypeVariables #-}
之后 class 定义将进行类型检查。
请注意,如果您使用 forall
关键字,此扩展仅适用于 "normal" 函数声明。因此,在 class 定义之外,您需要启用此扩展 和 写入:
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。