Reputation: 167
I defined the following data type:
type MySet a = (a -> Bool)
I want to use it to define a set of functions that have the following property: For all f in func3, f 3 == 2.
func3 :: MySet (Eq a => a -> a)
func3 = (\x -> (x 3) == 2)
The following error arises:
Illegal polymorphic or qualified type: Eq a => a -> a
Perhaps you intended to use -XLiberalTypeSynonyms
In the type signature for `func3': func3 :: MySet (Eq a => a -> a)
If I don't use the Eq typeclass, I get this error instead:
No instance for (Eq a) arising from a use of `=='
Possible fix:
add (Eq a) to the context of
the type signature for func3 :: MySet (a -> a)
Upvotes: 1
Views: 402
Reputation: 5992
Often the first step to working out a problem involving type synonyms (things that look like type ... = ...
) is to go through manually replacing the synonym (the left hand side of the type
expression) with its expansion (the right hand side). So:
func3 :: MySet (Eq a => a -> a)
becomes:
func4 :: (Eq a => a -> a) -> Bool
Now GHC gives us a more helpful error:
Illegal polymorphic or qualified type: Eq a => a -> a
Perhaps you intended to use RankNTypes or Rank2Types
In an expression type signature: (Eq a => a -> a) -> Bool
RankNTypes
is a fun extension, but it's not very helpful here. (I'll explain in a moment).
As n.m. pointed out, what you probably want to do is just move the constraints out of the parentheses:
func5 :: Eq a => (a -> a) -> Bool
And then GHC lets you know you also need Num
. Other than that, though, we have a totally ordinary-looking type signature: a constraint before the =>
, then regular type variables and a specific type. This is good; we generally want the types as straightforward as possible (but not more so).
But let's back up and examine two things: type synonyms and RankNTypes
.
First, you called MySet
a "data type." That's kind of a dangerous way to think of it. It's actually a type synonym, which means just what it sounds like: MySet (a -> a)
and (a -> a) -> Bool
are literally synonymous; they mean the same thing. Contrast something like data MySetD a = MySetD (a -> Bool)
. There, MySetD
is actually an honest-to-God data type that cannot be substituted, willy-nilly, for (a -> Bool)
and vice-versa.
Second: What's up with RankNTypes
? If we turn that extension on, we could write this:
funcRN :: (forall a. (Eq a, Num a) => a -> a) -> Bool
funcRN = (\x -> (x 3) == 2)
And it does what you want with things like funcRN ((+) 1)
. But what about this?
g :: Int -> Int
g x = x - 1
funcRN g
This doesn't work. funcRN
requires as its argument a function that can take any type a
such that a
is an instance of Eq
and Num
. That's what the forall a. (Eq a, Num a) =>
bit means. Since g
only works with one type, Int
, it's unacceptable to funcRN
's type signature.
Upvotes: 5