{-# LANGUAGE Safe #-}

{- |
Module                  : Relude.Extra.Bifunctor
Copyright               : (c) 2018-2023 Kowainik
SPDX-License-Identifier : MIT
Maintainer              : Kowainik <xrom.xkov@gmail.com>
Stability               : Stable
Portability             : Portable

Useful combinators for bifunctors inside functors. This set of functions is
useful when you want to work with types like these ones:

@
foo :: IO (Either a b)
bar :: IO (a, b)

baz :: Maybe (Either a b)
qux :: Maybe (a, b)

doo :: (a, a)
dee :: Either a a
@

@since 0.1.0
-}

module Relude.Extra.Bifunctor
    ( bimapBoth
    , bimapF
    , firstF
    , secondF
    ) where

import Relude

{- | Maps a function over both elements of a bifunctor.

>>> bimapBoth length ([True], [False, True])
(1,2)
>>> map (bimapBoth not) [Left True, Right False]
[Left False,Right True]

@since 0.6.0.0
-}
bimapBoth :: Bifunctor f => (a -> b) -> f a a -> f b b
bimapBoth :: forall (f :: * -> * -> *) a b.
Bifunctor f =>
(a -> b) -> f a a -> f b b
bimapBoth a -> b
f = (a -> b) -> (a -> b) -> f a a -> f b b
forall a b c d. (a -> b) -> (c -> d) -> f a c -> f b d
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap a -> b
f a -> b
f
{-# INLINE bimapBoth #-}

{- | Fmaps functions for nested bifunctor. Short for @fmap (bimap f g)@.

>>> bimapF not length $ Just (False, ['a', 'b'])
Just (True,2)

@since 0.1.0
-}
bimapF  :: (Functor f, Bifunctor p) => (a -> c) -> (b -> d) -> f (p a b) -> f (p c d)
bimapF :: forall (f :: * -> *) (p :: * -> * -> *) a c b d.
(Functor f, Bifunctor p) =>
(a -> c) -> (b -> d) -> f (p a b) -> f (p c d)
bimapF a -> c
f b -> d
g = (p a b -> p c d) -> f (p a b) -> f (p c d)
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> c) -> (b -> d) -> p a b -> p c d
forall a b c d. (a -> b) -> (c -> d) -> p a c -> p b d
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap a -> c
f b -> d
g)
{-# INLINE bimapF #-}

{- | Short for @fmap . first@.

>>> firstF not $ Just (False, ['a', 'b'])
Just (True,"ab")

@since 0.1.0
-}
firstF  :: (Functor f, Bifunctor p) => (a -> c) -> f (p a b) -> f (p c b)
firstF :: forall (f :: * -> *) (p :: * -> * -> *) a c b.
(Functor f, Bifunctor p) =>
(a -> c) -> f (p a b) -> f (p c b)
firstF = (p a b -> p c b) -> f (p a b) -> f (p c b)
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((p a b -> p c b) -> f (p a b) -> f (p c b))
-> ((a -> c) -> p a b -> p c b)
-> (a -> c)
-> f (p a b)
-> f (p c b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> c) -> p a b -> p c b
forall a b c. (a -> b) -> p a c -> p b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first
{-# INLINE firstF #-}

{- | Short for @fmap . second@.

>>> secondF length  $ Just (False, ['a', 'b'])
Just (False,2)

@since 0.1.0
-}
secondF  :: (Functor f, Bifunctor p) => (b -> d) -> f (p a b) -> f (p a d)
secondF :: forall (f :: * -> *) (p :: * -> * -> *) b d a.
(Functor f, Bifunctor p) =>
(b -> d) -> f (p a b) -> f (p a d)
secondF = (p a b -> p a d) -> f (p a b) -> f (p a d)
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((p a b -> p a d) -> f (p a b) -> f (p a d))
-> ((b -> d) -> p a b -> p a d)
-> (b -> d)
-> f (p a b)
-> f (p a d)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (b -> d) -> p a b -> p a d
forall b c a. (b -> c) -> p a b -> p a c
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second
{-# INLINE secondF #-}