{-|
Module      : DeepControl.Traversable
Description : Deepened the usual Data.Traversable module.
Copyright   : Conor McBride and Ross Paterson 2005,
              (c) 2015 KONISHI Yohsuke
License     : BSD-style (see the LICENSE file in the distribution)
Maintainer  : ocean0yohsuke@gmail.com
Stability   : experimental
Portability : ---

-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE TypeSynonymInstances #-}
module DeepControl.Traversable (
    -- * Utility functions
    -- ** Level-1
    sink, sinkmap,
    -- ** Level-2
    sink2, float2,
    -- ** Level-3
    sink3, float3,
    -- ** Level-4
    sink4, float4,
    -- ** Level-5
    sink5, float5,

    ) where

import DeepControl.Applicative

------------------------------------------------------------------------------
-- Level-1

-- | Synonym to @'Data.Traversable.sequenceA'@.
-- 
-- >>> sink $ Just [1]
-- [Just 1]
--
-- >>> sink $ Right (Just 1)
-- Just (Right 1)
--
-- >>> sink [[1,2],[3,4]]
-- [[1,3],[1,4],[2,3],[2,4]]
-- >>> sink . sink $ [[1,2],[3,4]]
-- [[1,1,2,2],[1,1,2,4],[1,1,3,2],[1,1,3,4],[1,4,2,2],[1,4,2,4],[1,4,3,2],[1,4,3,4],[3,1,2,2],[3,1,2,4],[3,1,3,2],[3,1,3,4],[3,4,2,2],[3,4,2,4],[3,4,3,2],[3,4,3,4]]
--
sink :: (Traversable t, Applicative f) => t (f a) -> f (t a)
sink = sequenceA

{-
instance Traversable ((->) r) where
    -- TODO: If GHC could parse this expression, maybe I could write up DeepControl.Monad.
    sequence ((r->) mv) = (r->) |$> mv
-}

-- | Alias to @'Data.Traversable.traversable'@.
sinkmap :: (Applicative f, Traversable c) => (a -> f b) -> c a -> f (c b)
sinkmap f = sink . (f |$>)

------------------------------------------------------------------------------
-- Level-2

-- | Definition: @ sink2 = (sink|$>) . sink @
--
-- >>> sink2 $ Just [[1]]
-- [[Just 1]]
--
-- >>> sink2 $ Right (Just [1])
-- Just [Right 1]
--
sink2 :: (Traversable m1, Applicative m2, Applicative m3) =>
         m1 (m2 (m3 a)) -> m2 (m3 (m1 a))
sink2 = (sink|$>) . sink

-- | Definition: @ float2 = sink . (sink|$>) @
--
-- >>> float2 $ [[Just 1]]
-- Just [[1]]
--
-- >>> float2 $ Just [Right 1]
-- Right (Just [1])
--
float2 :: (Applicative m1, Traversable m2, Traversable m3) =>
          m2 (m3 (m1 a)) -> m1 (m2 (m3 a))
float2 = sink . (sink|$>)

------------------------------------------------------------------------------
-- Level-3

-- | Definition: @ sink3 = (sink2|$>) . sink @
--
-- >>> sink3 $ Just [[[1]]]
-- [[[Just 1]]]
--
-- >>> sink3 $ Right [Just [1]]
-- [Just [Right 1]]
--
sink3 :: (Traversable m1, Applicative m2, Applicative m3, Applicative m4) =>
         m1 (m2 (m3 (m4 a))) -> m2 (m3 (m4 (m1 a)))
sink3 = (sink2|$>) . sink

-- | Definition: @ float3 = sink . (float2|$>) @
--
-- >>> float3 $ [[[Just 1]]]
-- Just [[[1]]]
--
-- >>> float3 $ [Just [Right 1]]
-- Right [Just [1]]
--
float3 :: (Applicative m1, Traversable m2, Traversable m3, Traversable m4) =>
          m2 (m3 (m4 (m1 a))) -> m1 (m2 (m3 (m4 a)))
float3 = sink . (float2|$>)

------------------------------------------------------------------------------
-- Level-4

sink4 :: (Traversable m1, Applicative m2, Applicative m3, Applicative m4, Applicative m5) =>
         m1 (m2 (m3 (m4 (m5 a)))) -> m2 (m3 (m4 (m5 (m1 a))))
sink4 = (sink3|$>) . sink

float4 :: (Applicative m1, Traversable m2, Traversable m3, Traversable m4, Traversable m5) =>
          m2 (m3 (m4 (m5 (m1 a)))) -> m1 (m2 (m3 (m4 (m5 a))))
float4 = sink . (float3|$>)

------------------------------------------------------------------------------
-- Level-5

sink5 :: (Traversable m1, Applicative m2, Applicative m3, Applicative m4, Applicative m5, Applicative m6) =>
         m1 (m2 (m3 (m4 (m5 (m6 a))))) -> m2 (m3 (m4 (m5 (m6 (m1 a)))))
sink5 = (sink4|$>) . sink

float5 :: (Applicative m1, Traversable m2, Traversable m3, Traversable m4, Traversable m5, Traversable m6) =>
          m2 (m3 (m4 (m5 (m6 (m1 a))))) -> m1 (m2 (m3 (m4 (m5 (m6 a)))))
float5 = sink . (float4|$>)