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
Functor
class. This is whySet
does not come with aFunctor
instance by default: you cannot write one.This is something of a problem, and there have been some suggested solutions (e.g. defining the
Functor
class 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
Functor
class using constraint kinds to reify the additional constraints instances of the newFunctor
class may have. This would let you specify thatSet
has to contain types from theOrd
class.Another approach uses only multi-parameter classes. I could only find the article about doing this for the
Monad
class, but makingSet
part ofMonad
faces 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
Set
also 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
Functor
needs two extensions:MultiParamTypeClasses
andFlexibleInstances
. (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.