Bifunctor
is a lot like Functor
. It offers a nice solution for those times when you don’t want to ignore the leftmost type argument of a binary type constructor, such as Either
or Tuple2
.
Its core operation, bimap
, closely resembles map
, except it lifts two functions into the new context, allowing you to apply one or both.
fun Kind2<F, A, B>.bimap(fl: (A) -> C, fr: (B) -> D): Kind2<F, C, D>
bimap
takes two unary functions and a binary type constructor as a receiver, such as Tuple2(1, 3)
or Left(5)
, and applies whichever function it can – both if possible!
import arrow.*
import arrow.core.*
import arrow.typeclasses.*
import arrow.core.extensions.either.bifunctor.*
fun <F> greet(BF: Bifunctor<F>, p: Kind2<F, String, String>): Kind2<F, String, String> =
BF.run { p.bimap({ "Hello $it" }, { "General $it" }) }
greet(Either.bifunctor(), Left("there")) // Left("Hello there")
// Either.Left(Hello there)
greet(Either.bifunctor(), Right("Kenobi")) // Right("General Kenobi")
// Either.Right(General Kenobi)
import arrow.core.extensions.tuple2.bifunctor.*
greet(Tuple2.bifunctor(), Tuple2("there", "Kenobi")) // Tuple2("Hello there", "General Kenobi")
// (Hello there, General Kenobi)
So, bimap
is map
, but for binary type constructors where you want the ability to lift two functions at once.
Transforms the inner contents of a binary type constructor.
fun Kind2<F, A, B>.bimap(fl: (A) -> C, fr: (B) -> D): Kind2<F, C, D>
val tuple2Bifunctor = Tuple2.bifunctor()
tuple2Bifunctor.run { Tuple2(4, 4).bimap({ it + 1 }, { it - 1 }) }
// (5, 3)
For a full list of other useful combinators available in Bifunctor
, see the Source
Arrow provides BifunctorLaws
in the form of test cases for internal verification of lawful instances and third party apps creating their own Bifunctor instances.
Do you like Arrow?
✖