Compiler doesn't pick up typeclass for the polymorphic constant value

送分小仙女□ 提交于 2019-12-24 10:37:38

问题


I'm new to Haskell, so forgive me in advance.

Why doesn't the following haskell code compile?

It seems like the compiler somehow fails to see that the type of the expression (maxBound :: a) is a which has an Enum instance provided, not some type variable ‘a0’ which is 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


回答1:


By default, even though type variables are scoped from the class being defined to the type signatures of the class's methods (i.e., the a in class SafeEnum a is the same a as the a in ssucc :: a -> a), they are not scoped from the type signatures of the methods to the method bodies, so in the expression maxBound :: a in the bodies of your functions ssucc and spred, the a has nothing to do with the a in the type signatures for those functions.

You can enable the ScopedTypeVariables extension, like so:

{-# LANGUAGE ScopedTypeVariables #-}

after which the class definition will type check.

Note that this extension only applies to "normal" function declarations if you use the forall keyword. So, outside of a class definition, you'd need to enable this extension and write:

ssucc :: forall a. a -> a 
ssucc x = ... maxBound :: a ...

or actually:

ssucc :: forall a. (Enum a, Bounded a) => a -> a
ssucc x = ... maxBound :: a ...

but the rules are different inside a class clause.

See the GHC docs for details.




回答2:


You need to add this line to the top of your file:

{-# LANGUAGE ScopedTypeVariables #-}

Without this extension turned on, maxBound :: a does not refer to the same a as in the class.

Essentially, in standard Haskell, each type signature has its own type variables, which are independent from any other variable. For instance, this code

foo :: [a] -> Int
foo xs = length ys
   where
   ys :: [a]
   ys = xs

fails, since ys :: [a] really means ys :: [b] with an independent variable b, and ys = xs does not produce a [b].

With the extension turned on, this compiles:

foo :: forall a . [a] -> Int
foo xs = length ys
   where
   ys :: [a]
   ys = xs

Arguably, there should be a different default, e.g. the extension should be on by default. Alternatively, GHC should hint at turning the extension on when the same a is used twice, since often that's the issue.



来源:https://stackoverflow.com/questions/55230741/compiler-doesnt-pick-up-typeclass-for-the-polymorphic-constant-value

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!