简体   繁体   中英

How to use a typeclass into a polymorphic data type?

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)

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 nm 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) => forall a. (Eq a, Num a) => bit means. Since g only works with one type, Int , it's unacceptable to funcRN 's type signature.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM