module Algebra.Traversable(
  module Algebra.Applicative, module Algebra.Foldable,

  Traversable(..),Contravariant(..),

  traverse,foreach,transpose,flip,project,doTimes,converted,folded,
  ) where

import Algebra.Classes
import Algebra.Core hiding (flip,(&))
import Algebra.Applicative
import Algebra.Foldable
import Algebra.Lens
import Data.Tree

class Foldable t => Traversable t where
  sequence :: Applicative f => t (f a) -> f (t a)
instance Traversable ((,) c) where
  sequence ~(c,m) = (,) c<$>m
instance Traversable (Either a) where
  sequence = pure . Left <|> map Right
instance Traversable [] where
  sequence (x:xs) = (:)<$>x<*>sequence xs
  sequence [] = pure []
deriving instance Traversable Interleave
deriving instance Traversable OrdList
deriving instance Traversable ZipList
instance Traversable Tree where
  sequence (Node a subs) = Node<$>a<*>sequence (map sequence subs)
deriving instance Traversable ZipTree
instance (Traversable f,Traversable g) => Traversable (f:.:g) where
  sequence = getCompose >>> map sequence >>> sequence >>> map Compose
instance (Traversable f,Traversable g) => Traversable (f:**:g) where
  sequence (f:**:g) = (:**:)<$>sequence f<*>sequence g
instance (Traversable f,Traversable g) => Traversable (f:++:g) where
  sequence (Sum (Left f)) = Sum . Left<$>sequence f
  sequence (Sum (Right g)) = Sum . Right<$>sequence g
instance Traversable Maybe where
  sequence Nothing = pure Nothing
  sequence (Just a) = Just<$>a

class Functor t => Contravariant t where
  collect :: Functor f => f (t a) -> t (f a)
instance Contravariant Id where collect f = Id (map getId f)
instance Contravariant ((->) a) where collect f = \a -> map ($a) f

converted :: (Unit f,Unit g,Foldable f,Foldable g,Monoid (f a),Monoid (g b)) => Iso (f a) (f b) (g a) (g b)
converted = iso convert convert
folded :: (Unit f',Foldable f,Monoid m) => Iso m m' (f m) (f' m')
folded = iso fold pure

traverse :: (Applicative f,Traversable t) => (a -> f b) -> t a -> f (t b)
traverse f t = sequence (map f t)
foreach :: (Applicative f,Traversable t) => t a -> (a -> f b) -> f (t b)
foreach = flip traverse
doTimes :: Applicative f => Int -> f a -> f [a]
doTimes n m = sequence (m <$ [1..n])
transpose :: (Applicative f,Traversable t) => t (f a) -> f (t a)
transpose = sequence
flip :: (Contravariant c,Functor f) => f (c a) -> c (f a)
flip = collect
-- | The Contravariant version of 'traverse'
project :: (Contravariant c,Functor f) => (a -> c b) -> f a -> c (f b)
project f x = collect (map f x)

instance Compound a b [a] [b] where
  _each = traverse