views:

102

answers:

3

I wrote something like this:

instance Functor (Either e) where

   fmap _ (Left a) = Left a

   fmap f (Right b) = Right (f b)

How do I do the same if I want fmap to change the value only if it's Left?

I mean, what syntax do I use to indicate that I use type Either _ b instead of Either a _?

+6  A: 

I don't think there's a way to do that directly, unfortunately. With a function you can use flip to partially apply the second argument, but that doesn't work with type constructors like Either.

The simplest thing is probably wrapping it in a newtype:

newtype Mirror b a = Mirrored (Either a b)

instance Functor (Mirror e) where
    fmap _ (Mirrored (Right a)) = Mirrored $ Right a
    fmap f (Mirrored (Left b)) = Mirrored $ Left (f b)

Wrapping with newtype is also the standard way to create multiple instances for a single type, such as Sum and Product being instances of Monoid for numeric types. Otherwise, you can only have one instance per type.

Additionally, depending on what it is you want to do, another option is to ignore Functor and define your own type class like this:

class Bifunctor f where
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d

instance Bifunctor Either where
    bimap f _ (Left a)  = Left  $ f a
    bimap _ g (Right b) = Right $ g b

instance Bifunctor (,) where
    bimap f g (a, b) = (f a, g b)

Obviously, that class is twice as much fun as a regular Functor. Of course, you can't make a Monad instance out of that very easily.

camccann
What about `newtype Flip t a b = Flip (t b a)` and then `instance Functor (Flip Either e)`?
Norman Ramsey
It doesn't work: "All instance types must be of the form (T a1 ... an) where a1 ... an are type *variables*"
m01
@m01: It works, but requires enabling a GHC language extension. In practice, I would either (no pun intended) do it the way Norman Ramsey suggests or use the `Bifunctor` class as in my answer. The over-specialized `Mirror` type was meant mostly to illustrate the idea.
camccann
m01: The extension you need is called `FlexibleInstances`.
Edward Kmett
+2  A: 

You essentially need a 'flip' combinator on types. A newtype wrapper that inverts the order should work, as camccann says. Note that you can't use a 'type' synonym, as they may not be partially applied.

Don Stewart
+3  A: 

You can't make the instance you are looking for directly.

In order for type inference and type classes to work, there is a certain positional bias to the ordering of arguments in the types. It has been shown that if we allowed arbitrary reordering of the arguments when instantiating type classes, that type inference becomes intractable.

You could use a Bifunctor class that can map over both arguments separately.

class Bifunctor f where
    bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
    first :: (a -> b) -> f a c -> f b c
    second :: (c -> d) -> f a c -> f a d

    first f = bimap f id
    second = bimap id

instance Bifunctor Either where
    bimap f _ (Left a) = Left (f a)
    bimap _ g (Right b) = Right (g b)

instance Bifunctor (,) where
    bimap f g (a,b) = (f a, g b)

Or you could use a Flip combinator like:

newtype Flip f a b = Flip { unFlip :: f b a }

Generalized versions of both of these are available in category-extras on hackage. The latter even includes an instance for Functor (Flip Either a) because Either is a Bifunctor. (I should probably fix that to only require a PFunctor)

Ultimately, the order of arguments in a type constructor is important in determining what classes you can instantiate. You may need to use newtype wrappers (like Flip above) to put the arguments where they need to be to qualify to construct an instance of another typeclass. This is the price we pay for the inference of type class constraints.

Edward Kmett