I've been trying to understand what a Functor is in Haskell, and for that I've want an example of a Functor, without any other properties.
The working example I came up with was
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor a $ g (b)
which I guess is a semi-practical Functor as the left value can be stored safely while the right value operated on. I then want to have the left value be replaced with the right value before the operation. So that...
Main> fmap (+2) MyFunctor 2 5
MyFunctor 5 7
Changing the instance declaration to do that however, yields an error.
data MyFunctor a b = MyFunctor a b deriving Show
instance Functor (MyFunctor a) where
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
Which I don't understand, or know what to search to find a similar enough question to help me.
C:\Haskell\func.hs:3:28: error:
* Couldn't match type `a1' with `a'
`a1' is a rigid type variable bound by
the type signature for:
fmap :: forall a1 b. (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
at C:\Haskell\func.hs:3:3
`a' is a rigid type variable bound by
the instance declaration at C:\Haskell\func.hs:2:10
Expected type: MyFunctor a b
Actual type: MyFunctor a1 b
* In the expression: MyFunctor b $ g (b)
In an equation for `fmap':
fmap g (MyFunctor a b) = MyFunctor b $ g (b)
In the instance declaration for `Functor (MyFunctor a)'
* Relevant bindings include
b :: a1 (bound at C:\Haskell\func.hs:3:23)
a :: a (bound at C:\Haskell\func.hs:3:21)
g :: a1 -> b (bound at C:\Haskell\func.hs:3:8)
fmap :: (a1 -> b) -> MyFunctor a a1 -> MyFunctor a b
(bound at C:\Haskell\func.hs:3:3)
I then want to have the left value be replace with the right value before the operation.
This is an illegal thing for a Functor to do: GHC is correctly telling you that the types don't work out. In particular, the left part of your MyFunctor
value may have a different type than the right part, as in MyFunctor 5 "hello"
. And since fmap
must operate on only the last type parameter of your type, it must therefore leave the first part alone. Let's look at a more specific example.
Recall that the type of fmap
is
fmap :: Functor f => (a -> b) -> f a -> f b
And suppose you have an object you'd like to fmap
over:
obj :: MyFunctor Int String
What, then, must be the type of f
to call fmap f obj
? To unify the involved types, we must have
f :: (String -> a)
and
fmap f obj :: MyFunctor Int a
So you can see that it is impossible to "replace" the first, Int, field with the previous value of the second, String field: the only thing allowed in that place is what was there before, an Int!
Now, you might imagine that you could make the types work out if you changed your MyFunctor definition so that the two fields have the same type:
data MyFunctor a = MyFunctor a a
-- Also illegal
instance Functor MyFunctor where
fmap f (MyFunctor a b) = MyFunctor b (f b)
But you can't do this either, because b
and f b
may be of different types, and the caller gets to choose what f
to use, so your implementation of fmap
may not assume they are the same.
In fact, for any given data type, there is at most one legal definition of Functor: if you find one which is legal, you can be certain that any other definition is illegal: either the types won't match up, or it will break one of the two Functor laws:
fmap id == id
fmap f . fmap g == fmap (f . g)
How might you break one of these laws while still having the types line up? By operating on some piece of the structure that is not part of the structure being fmap
ed over. For example, often someone writes a (bad!) Functor like this:
data Counter a = Counter Int a
instance Functor Counter where
fmap f (Counter n x) = Counter (n + 1) (f x)
But this breaks both of the Functor laws, because it allows you to count how many times fmap
has been called, which is supposed to be a detail which is not exposed.
fmap id (Counter 0 0) == Counter 1 0
(fmap tail . fmap tail) /= fmap (tail . tail)