Manipulating the order of arguments to type constructors
I wrote something like this:
instance Functor (Eithe开发者_StackOverflow中文版r 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 _
?
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.
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.
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.
精彩评论