编译器不会为多态常量值选取类型类

问题描述 投票:1回答:2

我是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

haskell typeclass
2个回答
5
投票

默认情况下,即使类型变量的范围从定义的类到类的方法的类型签名(即,a中的class SafeEnum aa中的a相同ssucc :: a -> a),它们的范围也不是类型签名的范围。方法体的方法,所以在函数maxBound :: assucc的体中的表达式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


2
投票

您需要将此行添加到文件的顶部:

{-# 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应该暗示将延长开启,因为通常这是问题。

© www.soinside.com 2019 - 2024. All rights reserved.