{-# LANGUAGE Safe          #-}
{-# LANGUAGE TupleSections #-}

{- |
Copyright:  (c) 2018-2021 Kowainik
SPDX-License-Identifier: MIT
Maintainer:  Kowainik <xrom.xkov@gmail.com>
Stability:   Experimental
Portability: Portable

Contains utility functions for working with tuples.

@since 0.4.0
-}

module Relude.Extra.Tuple
    ( dup
    , toFst
    , toSnd
    , fmapToFst
    , fmapToSnd
    , mapToFst
    , mapToSnd
    , traverseToFst
    , traverseToSnd
    , traverseBoth
    ) where

import Relude.Function ((.))
import Relude.Functor (Functor (..), (<$>))
import Relude.Applicative (Applicative (..))


-- $setup
-- >>> import Relude

{- | Creates a tuple by pairing something with itself.

>>> dup "foo"
("foo","foo")
>>> dup ()
((),())

@since 0.6.0.0
-}
dup :: a -> (a, a)
dup :: a -> (a, a)
dup a
a = (a
a, a
a)
{-# INLINE dup #-}

{- | Apply a function, with the result in the fst slot,
and the value in the other.

A dual to 'toSnd'.

>>> toFst length [3, 1, 0, 2]
(4,[3,1,0,2])
>>> toFst (+5) 10
(15,10)

@since 0.7.0.0
-}
toFst :: (a -> b) -> a -> (b, a)
toFst :: (a -> b) -> a -> (b, a)
toFst a -> b
f a
a = (a -> b
f a
a, a
a)
{-# INLINE toFst #-}

{- | Apply a function, with the result in the snd slot,
and the value in the other.

A dual to 'toFst'.

>>> toSnd length [3, 1, 0, 2]
([3,1,0,2],4)
>>> toSnd (+5) 10
(10,15)

@since 0.7.0.0
-}
toSnd :: (a -> b) -> a -> (a, b)
toSnd :: (a -> b) -> a -> (a, b)
toSnd a -> b
f a
a = (a
a, a -> b
f a
a)
{-# INLINE toSnd #-}

{- | Like 'fmap', but also keep the original value in the snd position.

A dual to 'fmapToSnd'.

>>> fmapToFst show [3, 10, 2]
[("3",3),("10",10),("2",2)]

@since 0.7.0.0
-}
fmapToFst :: Functor f => (a -> b) -> f a -> f (b, a)
fmapToFst :: (a -> b) -> f a -> f (b, a)
fmapToFst = (a -> (b, a)) -> f a -> f (b, a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> (b, a)) -> f a -> f (b, a))
-> ((a -> b) -> a -> (b, a)) -> (a -> b) -> f a -> f (b, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> a -> (b, a)
forall a b. (a -> b) -> a -> (b, a)
toFst
{-# INLINE fmapToFst #-}

{- | Like 'fmap', but also keep the original value in the fst position.

A dual to 'fmapToFst'.

>>> fmapToSnd show [3, 10, 2]
[(3,"3"),(10,"10"),(2,"2")]

@since 0.7.0.0
-}
fmapToSnd :: Functor f => (a -> b) -> f a -> f (a, b)
fmapToSnd :: (a -> b) -> f a -> f (a, b)
fmapToSnd = (a -> (a, b)) -> f a -> f (a, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> (a, b)) -> f a -> f (a, b))
-> ((a -> b) -> a -> (a, b)) -> (a -> b) -> f a -> f (a, b)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> b) -> a -> (a, b)
forall a b. (a -> b) -> a -> (a, b)
toSnd
{-# INLINE fmapToSnd #-}

{- | Apply a function, with the result in the fst slot,
and the value in the other.

A dual to 'mapToSnd'

>>> mapToFst (+1) 10
(11,10)

@since 0.4.0
-}
mapToFst :: (a -> b) -> a -> (b, a)
mapToFst :: (a -> b) -> a -> (b, a)
mapToFst = (a -> b) -> a -> (b, a)
forall a b. (a -> b) -> a -> (b, a)
toFst
{-# INLINE mapToFst #-}
{-# DEPRECATED mapToFst "Use 'toFst' from 'Relude.Extra.Tuple' instead" #-}

{- | Apply a function, with the result in the second slot,
and the value in the other.

A dual to 'mapToFst'.

>>> mapToSnd (+1) 10
(10,11)

@since 0.4.0
-}
mapToSnd :: (a -> b) -> a -> (a, b)
mapToSnd :: (a -> b) -> a -> (a, b)
mapToSnd = (a -> b) -> a -> (a, b)
forall a b. (a -> b) -> a -> (a, b)
toSnd
{-# INLINE mapToSnd #-}
{-# DEPRECATED mapToSnd "Use 'toSnd' from 'Relude.Extra.Tuple' instead" #-}

{- | Apply a function that returns a value inside of a functor,
with the output in the first slot, the input in the second,
and the entire tuple inside the functor.


A dual to 'traverseToSnd'

>>> traverseToFst (Just . (+1)) 10
Just (11,10)
>>> traverseToFst (const Nothing) 10
Nothing

@since 0.5.0
-}
traverseToFst :: Functor t => (a -> t b) -> a -> t (b, a)
traverseToFst :: (a -> t b) -> a -> t (b, a)
traverseToFst a -> t b
f a
a = (,a
a) (b -> (b, a)) -> t b -> t (b, a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> t b
f a
a
{-# INLINE traverseToFst #-}

{- | Apply a function that returns a value inside of a functor,
with the output in the second slot, the input in the first,
and the entire tuple inside the functor.

A dual to 'traverseToFst'.

>>> traverseToSnd (Just . (+1)) 10
Just (10,11)
>>> traverseToSnd (const Nothing) 10
Nothing

@since 0.5.0
-}
traverseToSnd :: Functor t => (a -> t b) -> a -> t (a, b)
traverseToSnd :: (a -> t b) -> a -> t (a, b)
traverseToSnd a -> t b
f a
a = (a
a,) (b -> (a, b)) -> t b -> t (a, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> t b
f a
a
{-# INLINE traverseToSnd #-}

{- | Maps a function that returns a value inside of
an applicative functor over both elements of a tuple,
with the entire tuple inside the applicative functor.

>>> traverseBoth (Just . ("Hello " <>)) ("Alice", "Bob")
Just ("Hello Alice","Hello Bob")
>>> traverseBoth (const Nothing) ("Alice", "Bob")
Nothing
-}
traverseBoth :: Applicative t => (a -> t b) -> (a, a) -> t (b, b)
traverseBoth :: (a -> t b) -> (a, a) -> t (b, b)
traverseBoth a -> t b
f (a
a1, a
a2) = (,) (b -> b -> (b, b)) -> t b -> t (b -> (b, b))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> t b
f a
a1 t (b -> (b, b)) -> t b -> t (b, b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> a -> t b
f a
a2
{-# INLINE traverseBoth #-}