Bifunctor
Common instances of Bifunctor
Section titled “Common instances of Bifunctor”Two-element tuples
Section titled “Two-element tuples”(,) is an example of a type that has a Bifunctor instance.
instance Bifunctor (,) where bimap f g (x, y) = (f x, g y)bimap takes a pair of functions and applies them to the tuple’s respective components.
bimap (+ 2) (++ "nie") (3, "john") --> (5,"johnnie")bimap ceiling length (3.5 :: Double, "john" :: String) --> (4,4)Either
Section titled “Either”Either’s instance of Bifunctor selects one of the two functions to apply depending on whether the value is Left or Right.
instance Bifunctor Either where bimap f g (Left x) = Left (f x) bimap f g (Right y) = Right (g y)Definition of Bifunctor
Section titled “Definition of Bifunctor”Bifunctor is the class of types with two type parameters (f :: * -> * -> *), both of which can be covariantly mapped over simultaneously.
class Bifunctor f where bimap :: (a -> c) -> (b -> d) -> f a b -> f c dbimap can be thought of as applying a pair of fmap operations to a datatype.
A correct instance of Bifunctor for a type f must satisfy the bifunctor laws, which are analogous to the functor laws:
bimap id id = id -- identitybimap (f . g) (h . i) = bimap f h . bimap g i -- compositionThe Bifunctor class is found in the Data.Bifunctor module. For GHC versions >7.10, this module is bundled with the compiler; for earlier versions you need to install the bifunctors package.
first and second
Section titled “first and second”If mapping covariantly over only the first argument, or only the second argument, is desired, then first or second ought to be used (in lieu of bimap).
first :: Bifunctor f => (a -> c) -> f a b -> f c bfirst f = bimap f id
second :: Bifunctor f => (b -> d) -> f a b -> f a dsecond g = bimap id gFor example,
ghci> second (+ 2) (Right 40)Right 42ghci> second (+ 2) (Left "uh oh")Left "uh oh"Syntax
Section titled “Syntax”- bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
- first :: (a -> b) -> p a c -> p b c
- second :: (b -> c) -> p a b -> p a c
Remarks
Section titled “Remarks”A run of the mill Functor is covariant in a single type parameter. For instance, if f is a Functor, then given an f a, and a function of the form a -> b, one can obtain an f b (through the use of fmap).
A Bifunctor is covariant in two type parameters. If f is a Bifunctor, then given an f a b, and two functions, one from a -> c, and another from b -> d, then one can obtain an f c d (using bimap).
first should be thought of as an fmap over the first type parameter, second as an fmap over the second, and bimap should be conceived as mapping two functions covariantly over the first and second type parameters, respectively.