The reason why Set
is not a functor is given here. It seems to boil down to the fact that a == b && f a /= f b
is possible. So, why doesn't Haskell have as standard an alternative to Eq, something like
class Eq a => StrongEq a where
(===) :: a -> a -> Bool
(/==) :: a -> a -> Bool
x /== y = not (x === y)
x === y = not (x /== y)
for which instances are supposed to obey the laws
∀a,b,f. not (a === b) || (f a === f b)
∀a. a === a
∀a,b. (a === b) == (b === a)
and maybe some others? Then we could have:
instance StrongEq a => Functor (Set a) where
-- ...
Or am I missing something?
Edit: my problem is not “Why are there types without an Eq
instance?”, like some of you seem to have answered. It's the opposite: “Why are there instances of Eq
that aren't extensionally equal? Why are there too many Eq
instances?”, combined with “If a == b
does imply extensional equality, why is Set
not an instance of Functor
?”.
Also, my instance declaration is rubbish (thanks @n.m.). I should have said:
newtype StrongSet a = StrongSet (Set a)
instance Functor StrongSet where
fmap :: (StrongEq a, StrongEq b) => (a -> b) -> StrongSet a -> StrongSet b
fmap (StrongSet s) = StrongSet (map s)
Since I'm not a category theorist, I'll try to write a more concrete/practical explanation (i.e., one I can understand):
The key point is the one that @leftaroundabout made in a comment:
That is, there should be no need for your
StrongEq
because that's whatEq
is supposed to be already.Haskell values are often intended to represent some sort of mathematical or "real-life" value. Many times, this representation is one-to-one. For example, consider the type
This type contains exactly one representation of each Platonic solid. We can take advantage of this by adding
deriving Eq
to the declaration, and it will produce the correct instance.In many cases, however, the same abstract value may be represented by more than one Haskell value. For example, the red-black trees
Node B (Node R Leaf 1 Leaf) 2 Leaf
andNode B Leaf 1 (Node R Leaf 2 Leaf)
can both represent the set {1,2}. If we addedderiving Eq
to our declaration, we would get an instance ofEq
that distinguishes things we want to be considered the same (outside of the implementation of the set operations).It's important to make sure that types are only made instances of
Eq
(andOrd
) when appropriate! It's very tempting to make something an instance ofOrd
just so you can stick it in a data structure that requires ordering, but if the ordering is not truly a total ordering of the abstract values, all manner of breakage may ensue. Unless the documentation absolutely guarantees it, for example, a function calledsort :: Ord a => [a] -> [a]
may not only be an unstable sort, but may not even produce a list containing all the Haskell values that go into it.sort [Bad 1 "Bob", Bad 1 "James"]
can reasonably produce[Bad 1 "Bob", Bad 1 "James"]
,[Bad 1 "James", Bad 1 "Bob"]
,[Bad 1 "James", Bad 1 "James"]
, or[Bad 1 "Bob", Bad 1 "Bob"]
. All of these are perfectly legitimate. A function that usesunsafePerformIO
in the back room to implement a Las Vegas-style randomized algorithm or to race threads against each other to get an answer from the fastest may even give different results different times, as long as they're==
to each other.tl;dr: Making something an instance of
Eq
is a way of making a very strong statement to the world; don't make that statement if you don't mean it.This notion of
StrongEq
is tough. In general, equality is a place where computer science becomes significantly more rigorous than typical mathematics in the kind of way which makes things challenging.In particular, typical mathematics likes to talk about objects as though they exist in a set and can be uniquely identified. Computer programs usually deal with types which are not always computable (as a simple counterexample, tell me what the set corresponding to the type
data U = U (U -> U)
is). This means that it may be undecidable as to whether two values are identifiable.This becomes an enormous topic in dependently typed languages since typechecking requires identifying like types and dependently typed languages may have arbitrary values in their types and thus need a way to project equality.
So,
StrongEq
could be defined over a restricted part of Haskell containing only the types which can be decidably compared for equality. We can consider this a category with the arrows as computable functions and then seeSet
as an endofunctor from types to the type of sets of values of that type. Unfortunately, these restrictions have taken us far from standard Haskell and make definingStrongEq
orFunctor (Set a)
a little less than practical.Your second
Functor
instance also doesn't make any sense. The biggest reason whySet
can't be aFunctor
in Haskell isfmap
can't have constraints. Inventing different notions of equality asStrongEq
doesn't change the fact that you can't write those constraints onfmap
in your Set instance.fmap
in general shouldn't have the constraints you need. It makes perfect sense to have functors of functions, for example (without it the whole notion of using Applicative to apply functions inside a functor breaks down), and functions can't be members of Eq or your StrongEq in general.fmap
can't have extra constraints on only some instances, because of code like this:This code claims to work regardless of the functors
f
andg
, and regardless of the functionsh
andj
. It has no way of checking whether one of the functors is a special one that has extra constraints onfmap
, nor any way of checking whether one of the functions it's applying would violate those constraints.Saying that Set is a Functor in Haskell, is saying that there is a (lawful) operation
fmap :: (a -> b) -> Set a -> Set b
, with that exact type. That is precisely whatFunctor
means.fmap :: (Eq a -> Eq b) => (a -> b) -> Set a -> Set b
is not an example of such an operation.It is possible, I understand, to use the ConstraintKinds GHC extendsion to write a different Functor class that permits constraints on the values which vary by Functor (and what you actually need is an
Ord
constraint, not justEq
). This blog post talks about doing so to make a new Monad class which can have an instance for Set. I've never played around with code like this, so I don't know much more than that the technique exists. It wouldn't help you hand off Sets to existing code that needs Functors, but you should be able to use it instead of Functor in your own code if you wish.This makes sense neither in Haskell nor in the grand mathematical/categorical scheme of things, regardless of what
StrongEq
means.In Haskell,
Functor
requires a type constructor of kind* -> *
. The arrow reflects the fact that in category theory, a functor is a kind of mapping.[]
and (the hypothetical)Set
are such type constructors.[a]
andSet a
have kind*
and cannot be functors.In Haskell, it is hard to define
Set
such that it can be made into aFunctor
because equality cannot be sensibly defined for some types no matter what. You cannot compare two things of typeInteger->Integer
, for example.Let's suppose there is a function
Suppose you have a value
s :: Set Integer
. Whatfmap goedel s
should look like? How do you eliminate duplicates?In your typical set theory equality is magically defined for everything, including functions, so
Set
(orPowerset
to be precise) is a functor, no problem with that.