开发者

Manipulating the order of arguments to type constructors

开发者 https://www.devze.com 2022-12-21 10:55 出处:网络
I wrote something like this: instance Functor (Eithe开发者_StackOverflow中文版r e) where fmap _ (Left a) = Left a

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消