-- |
-- Module      : Basement.Mappable
-- License     : BSD-style
-- Maintainer  : Nicolas Di Prima <nicolas@primetype.co.uk>
-- Stability   : experimental
-- Portability : portable
--
-- Class of collection that can be traversed from left to right,
-- performing an action on each element.
--
module Foundation.Collection.Mappable
    ( Mappable(..)
    , sequence_
    , traverse_
    , mapM_
    , forM
    , forM_
    ) where

import           Basement.Compat.Base
import qualified Data.Traversable
import           Basement.BoxedArray (Array)

-- | Functors representing data structures that can be traversed from
-- left to right.
--
-- Mostly like base's `Traversable` but applied to collections only.
--
class Functor collection => Mappable collection where
    {-# MINIMAL traverse | sequenceA #-}

    -- | Map each element of a structure to an action, evaluate these actions
    -- from left to right, and collect the results. For a version that ignores
    -- the results see 'Foundation.Collection.traverse_'.
    traverse :: Applicative f => (a -> f b)
                              -> collection a
                              -> f (collection b)
    traverse a -> f b
f = collection (f b) -> f (collection b)
forall (collection :: * -> *) (f :: * -> *) a.
(Mappable collection, Applicative f) =>
collection (f a) -> f (collection a)
sequenceA (collection (f b) -> f (collection b))
-> (collection a -> collection (f b))
-> collection a
-> f (collection b)
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (a -> f b) -> collection a -> collection (f b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> f b
f

    -- | Evaluate each actions of the given collections, from left to right,
    -- and collect the results. For a version that ignores the results, see
    -- `Foundation.Collection.sequenceA_`
    sequenceA :: Applicative f => collection (f a)
                               -> f (collection a)
    sequenceA = (f a -> f a) -> collection (f a) -> f (collection a)
forall (collection :: * -> *) (f :: * -> *) a b.
(Mappable collection, Applicative f) =>
(a -> f b) -> collection a -> f (collection b)
traverse f a -> f a
forall k (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id

    -- | Map each element of the collection to an action, evaluate these actions
    -- from left to right, and collect the results. For a version that ignores
    -- the results see 'Foundation.Collection.mapM_'.
    mapM :: (Applicative m, Monad m) => (a -> m b) -> collection a -> m (collection b)
    mapM = (a -> m b) -> collection a -> m (collection b)
forall (collection :: * -> *) (f :: * -> *) a b.
(Mappable collection, Applicative f) =>
(a -> f b) -> collection a -> f (collection b)
traverse

    -- | Evaluate each actions of the given collections, from left to right,
    -- and collect the results. For a version that ignores the results, see
    -- `Foundation.Collection.sequence_`
    sequence :: (Applicative m, Monad m) => collection (m a) -> m (collection a)
    sequence = collection (m a) -> m (collection a)
forall (collection :: * -> *) (f :: * -> *) a.
(Mappable collection, Applicative f) =>
collection (f a) -> f (collection a)
sequenceA

-- | Map each element of a collection to an action, evaluate these
-- actions from left to right, and ignore the results. For a version
-- that doesn't ignore the results see 'Foundation.Collection.traverse`
traverse_ :: (Mappable col, Applicative f) => (a -> f b) -> col a -> f ()
traverse_ :: (a -> f b) -> col a -> f ()
traverse_ a -> f b
f col a
col = (a -> f b) -> col a -> f (col b)
forall (collection :: * -> *) (f :: * -> *) a b.
(Mappable collection, Applicative f) =>
(a -> f b) -> collection a -> f (collection b)
traverse a -> f b
f col a
col f (col b) -> f () -> f ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> () -> f ()
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()

-- | Evaluate each action in the collection from left to right, and
-- ignore the results. For a version that doesn't ignore the results
-- see 'Foundation.Collection.sequenceA'.
--sequenceA_ :: (Mappable col, Applicative f) => col (f a) -> f ()
--sequenceA_ col = sequenceA col *> pure ()

-- | Map each element of a collection to a monadic action, evaluate
-- these actions from left to right, and ignore the results. For a
-- version that doesn't ignore the results see
-- 'Foundation.Collection.mapM'.
mapM_ :: (Mappable col, Applicative m, Monad m) => (a -> m b) -> col a -> m ()
mapM_ :: (a -> m b) -> col a -> m ()
mapM_ a -> m b
f col a
c = (a -> m b) -> col a -> m (col b)
forall (collection :: * -> *) (m :: * -> *) a b.
(Mappable collection, Applicative m, Monad m) =>
(a -> m b) -> collection a -> m (collection b)
mapM a -> m b
f col a
c m (col b) -> m () -> m ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | Evaluate each monadic action in the collection from left to right,
-- and ignore the results. For a version that doesn't ignore the
-- results see 'Foundation.Collection.sequence'.
sequence_ :: (Mappable col, Applicative m, Monad m) => col (m a) -> m ()
sequence_ :: col (m a) -> m ()
sequence_ col (m a)
c = col (m a) -> m (col a)
forall (collection :: * -> *) (m :: * -> *) a.
(Mappable collection, Applicative m, Monad m) =>
collection (m a) -> m (collection a)
sequence col (m a)
c m (col a) -> m () -> m ()
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> () -> m ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- | 'forM' is 'mapM' with its arguments flipped. For a version that
-- ignores the results see 'Foundation.Collection.forM_'.
forM :: (Mappable col, Applicative m, Monad m) => col a -> (a -> m b) -> m (col b)
forM :: col a -> (a -> m b) -> m (col b)
forM = ((a -> m b) -> col a -> m (col b))
-> col a -> (a -> m b) -> m (col b)
forall a b c. (a -> b -> c) -> b -> a -> c
flip (a -> m b) -> col a -> m (col b)
forall (collection :: * -> *) (m :: * -> *) a b.
(Mappable collection, Applicative m, Monad m) =>
(a -> m b) -> collection a -> m (collection b)
mapM

-- | 'forM_' is 'mapM_' with its arguments flipped. For a version that
-- doesn't ignore the results see 'Foundation.Collection.forM'.
forM_ :: (Mappable col, Applicative m, Monad m) => col a -> (a -> m b) -> m ()
forM_ :: col a -> (a -> m b) -> m ()
forM_ = ((a -> m b) -> col a -> m ()) -> col a -> (a -> m b) -> m ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip (a -> m b) -> col a -> m ()
forall (col :: * -> *) (m :: * -> *) a b.
(Mappable col, Applicative m, Monad m) =>
(a -> m b) -> col a -> m ()
mapM_

----------------------------
-- Foldable instances
----------------------------

instance Mappable [] where
    {-# INLINE traverse #-}
    traverse :: (a -> f b) -> [a] -> f [b]
traverse = (a -> f b) -> [a] -> f [b]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
Data.Traversable.traverse

instance Mappable Array where
    -- | TODO: to optimise
    traverse :: (a -> f b) -> Array a -> f (Array b)
traverse a -> f b
f Array a
arr = [b] -> Array b
forall l. IsList l => [Item l] -> l
fromList ([b] -> Array b) -> f [b] -> f (Array b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (a -> f b) -> [a] -> f [b]
forall (collection :: * -> *) (f :: * -> *) a b.
(Mappable collection, Applicative f) =>
(a -> f b) -> collection a -> f (collection b)
traverse a -> f b
f (Array a -> [Item (Array a)]
forall l. IsList l => l -> [Item l]
toList Array a
arr)