bifunctor는 타입인자가 두개인 타입생성자에 대해 각각 적용가능한 함수 두 개를 인자로 받아 lifting하는 bimap함수를 제공한다.
Either l1 r1
에 대한 fmap과 bimap은 각각 다음과 같다.
fmap :: (r1 -> r2)
-> (Either l1 r1) -> (Either l1 -> r2)
bimap :: (l1 -> l2) -> (r1 -> r2)
-> (Either l1 r1) -> (Either l2 -> r2)
right 타입뿐만 아니라 left 타입에 대해서도 사상이 필요하다면 bimap을 사용할 수 있다. 또한 fmap = bimap id
과 동일하며 lmap f = bimap f id
를 사용하면 첫번째 타입 인자에 대해서만 사상할 수 도 있다.
bifunctor 역시도 functor이기때문에 지켜져야 할 laws가 있다.
bimap id id == id -- identity
(bimap f1 g1) . (bimap f2 g2) == bimap (f1 . f2) (g1 . g2) -- composition
fmap(covariant functor), contramap(contravariant functor), bimap(bifunctor)에 대해서 이해했다면 dimap(profunctor)는 어렵지 않다. 첫번째 인자가 contravariant, 두번째 인자가 covariant인 bifunctor가 profunctor이다.
bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
-- ^^^^^^
dimap :: (b -> a) -> (c -> d) -> f a c -> f b d
-- ^^^^^^
(출처: Understading productors)
Either, Tuple의 첫번째 타입 인자는 covariant이기때문에 profunctor가 아니지만 (->)의 첫번째 인자는 contravariant이기 때문에 profunctor이다.
f가 (->) 이고 infix notation으로 dimap을 표현하면 이렇게 된다.
dimap :: (b -> a) -> (c -> d)
-> (a -> c) -- f a c
-> (b -> d) -- f b d