Reputation:
The following is quoted from the GHC user guide (Haskell Platform 2012.4.0.0)...
7.6.1.3. Class method types
Haskell 98 prohibits class method types to mention constraints on the class type variable, thus:
class Seq s a where fromList :: [a] -> s a elem :: Eq a => a -> s a -> Bool
The type of elem is illegal in Haskell 98, because it contains the constraint Eq a, constrains only the class type variable (in this case a). GHC lifts this restriction (flag -XConstrainedClassMethods).
However, I don't see any explanation of what this means. I can see two possibilities...
Seq
type class implicitly gains the Eq a
constraint from elem
.elem
method cannot be used for type class Seq
in cases where a
is not a member of class Eq
(or where it is a member, but that's unknown where elem
is used).I strongly suspect (2) because it seems potentially useful whereas (1) seems useless. (2) basically allows methods to be defined for cases where they can be supported, without limiting the typeclass to only being instanced for those cases.
The example seems to motivate exactly this - the idea being that elem
is often a useful operation for sequences, too valuable to live without, yet we also want to support those sequences where elem
is unsupportable, such as sequences of functions.
Am I right, or have I missed something? What are the semantics for this extension?
Upvotes: 6
Views: 705
Reputation: 15068
NOTE: it seems like there is a bug in GHC >= 7 that makes GHC accept constrained class methods even in Haskell 98 mode.
Additionally, the example from the manual is always accepted when MultiParamTypeClasses
are enabled, regardless of whether the ConstrainedMethodTypes
extension is on (also likely a bug).
In the type of elem
:
elem :: Eq a => a -> s a -> Bool
a
and s
are class type variables, and Eq a
is a constraint on the class type variable a
. As the manual says, Haskell 98 prohibits such constraints (FWIW, it also prohibits multi-parameter type classes). Therefore, the following code shouldn't be accepted in the Haskell 98 mode (and I think it's also prohibited in Haskell 2010):
class Compare a where
comp :: Eq a => a -> a -> Bool
And indeed, GHC 6.12.1 rejects it:
Prelude> :load Test.hs
[1 of 1] Compiling Main ( Test.hs, interpreted )
Test.hs:3:0:
All of the type variables in the constraint `Eq a'
are already in scope (at least one must be universally quantified here)
(Use -XFlexibleContexts to lift this restriction)
When checking the class method: comp :: (Eq a) => a -> a -> Bool
In the class declaration for `Compare'
Failed, modules loaded: none.
Prelude> :set -XConstrainedClassMethods
Prelude> :load Test.hs
[1 of 1] Compiling Main ( Test.hs, interpreted )
Ok, modules loaded: Main.
The idea is that superclass constraints should be used instead:
class (Eq a) => Compare a where
comp :: a -> a -> Bool
Regarding semantics, you can easily check whether a class method constraint implicitly adds a superclass constraint with the following code:
{-# LANGUAGE ConstrainedClassMethods #-}
module Main where
class Compare a where
comp :: (Eq a) => a -> a -> Bool
someMethod :: a -> a -> a
data A = A deriving Show
data B = B deriving (Show,Eq)
instance Compare A where
comp = undefined
someMethod A A = A
instance Compare B where
comp = (==)
someMethod B B = B
Testing with GHC 6.12.1:
*Main> :load Test.hs
[1 of 1] Compiling Main ( Test.hs, interpreted )
Ok, modules loaded: Main.
*Main> comp A
<interactive>:1:0:
No instance for (Eq A)
arising from a use of `comp' at <interactive>:1:0-5
Possible fix: add an instance declaration for (Eq A)
In the expression: comp A
In the definition of `it': it = comp A
*Main> someMethod A A
A
*Main> comp B B
True
Answer: no, it doesn't. The constraint applies only to the method that has a constrained type. So you are right, it's the possibility #2.
Upvotes: 7