I'm currently in Chapter 8 of Learn you a Haskell, and I've reached the section on the Functor typeclass. In said section the author gives examples of how different types could be made instances of the class (e.g Maybe, a custom Tree type, etc.) Seeing this, I decided to (for fun and practice) try implementing an instance for the Data.Set type; in all of this ignoring Data.Set.map, of course.
The actual instance itself is pretty straight-forward, and I wrote it as:
instance Functor Set.Set where
fmap f empty = Set.empty
fmap f s = Set.fromList $ map f (Set.elems s)
But, since I happen to use the function fromList this brings in a class constraint calling for the types used in the Set to be Ord, as is explained by a compiler error:
Error occurred
ERROR line 4 - Cannot justify constraints in instance member binding
*** Expression : fmap
*** Type : Functor Set => (a -> b) -> Set a -> Set b
*** Given context : Functor Set
*** Constraints : Ord b
See: Live Example
I tried putting a constraint on the instance, or adding a type signature to fmap, but neither succeeded (both were compiler errors as well.)
Given a situation like this, how can a constraint be fulfilled and satisfied? Is there any possible way?
Thanks in advance! :)
Unfortunately, there is no easy way to do this with the standard
Functorclass. This is whySetdoes not come with aFunctorinstance by default: you cannot write one.This is something of a problem, and there have been some suggested solutions (e.g. defining the
Functorclass in a different way), but I do not know if there is a consensus on how to best handle this.I believe one approach is to rewrite the
Functorclass using constraint kinds to reify the additional constraints instances of the newFunctorclass may have. This would let you specify thatSethas to contain types from theOrdclass.Another approach uses only multi-parameter classes. I could only find the article about doing this for the
Monadclass, but makingSetpart ofMonadfaces the same problems as making it part ofFunctor. It's called Restricted Monads.The basic gist of using multi-parameter classes here seems to be something like this:
Essentially, all you're doing here is making the types in the
Setalso part of the class. This then lets you constrain what these types can be when you write an instance of that class.This version of
Functorneeds two extensions:MultiParamTypeClassesandFlexibleInstances. (You need the first extension to be able to define the class and the second extension to be able to define an instance forSet.)Haskell : An example of a Foldable which is not a Functor (or not Traversable)? has a good discussion about this.