Before reading this typeclass, we recommend that you understand Contravariance first. But, for making things easier, we will consider Contravariance as the ability to flip composition.
Profunctors are Bifunctors that are contravariant in their first argument and covariant in the second one.
The core operation of the Profunctor typeclass is dimap (as bimap was already taken for Bifunctor).
fun <A, B, C, D> Kind2<F, A, B>.dimap(fl: (C) -> A, fr: (B) -> D): Kind2<F, C, D>
The main difference between bimap and dimap is the function they accept as their first argument:
bimap: fl: (A) -> Cdimap: fl: (C) -> AAnd how does this work? Well, if we think in terms of function composition, functions can be composed in both directions:
import arrow.core.*
val sum2: (Int) -> Int = { x -> x + 2 }
val str: (Int) -> String = { x -> x.toString() }
val f = str compose sum2
val g = sum2 andThen str
f(4) == g(4)
// true
So, if we have a function (A) -> B and a Profunctor instance for it, we can make the following transformation with dimap: ((C) -> A) -> ((A) -> B) -> ((B) -> D).
Contramap on the first type parameter and map on the second type parameter.
fun Kind2<F, A, B>.dimap(fl: (C) -> A, fr: (B) -> D): Kind2<F, C, D>
For a full list of other useful combinators available in Profunctor, see the Source
Arrow provides ProfunctorLaws in the form of test cases for internal verification of lawful instances and third party apps creating their own Profunctor instances.
Do you like Arrow?
✖