module Data.Apart.Combinators (Restorer, Materializer, recover, limit, throughout, inmemory) where
import "base" Control.Applicative (Alternative (..))
import "base" Control.Monad (join)
import "free" Control.Comonad.Cofree (Cofree (..))
import Data.Apart (Apart (..))
import Data.Apart.Shape (Shape (..))
import Data.Apart.Transformations (Segmented (..), Scattered (..))
type Restorer g t raw value = (Traversable t, Applicative g) =>
raw -> g (Segmented (Cofree t) value)
type Materializer g t raw value = (Traversable t, Applicative g) =>
Segmented (Cofree t) value -> g raw
recover :: (Traversable t, Applicative g) => Restorer g t raw value
-> Scattered (Cofree t) value raw -> g (Cofree t value)
recover convert (Apart (x :< Ready values)) = (:<) x <$>
traverse (recover convert . Apart) values
recover convert (Apart (x :< Converted raw)) = (:<) x <$> convert raw
limit :: (Traversable t, Applicative g) => Int -> Materializer g t raw value
-> Cofree t value -> g (Scattered (Cofree t) value raw)
limit ((>=) 0 -> True) convert (x :< rest) = error "Limit value should be greater than 0"
limit 1 convert (x :< rest) = (Apart . (:<) x . Converted) <$> convert rest
limit n convert (x :< rest) = (<$>) (Apart . (:<) x . Ready) $
((<$>) . (<$>)) part $ traverse (limit (n - 1) convert) rest
throughout :: (Traversable t, Monad g) => (value -> g result) -> Restorer g t raw value
-> (Scattered (Cofree t) value raw) -> g (Cofree t result)
throughout f g (Apart (x :< Ready vs)) = (:<) <$> f x <*> (traverse (throughout f g . Apart) vs)
throughout f g (Apart (x :< Converted r)) = join $ traverse f <$> ((:<) x <$> g r)
inmemory :: (Functor t, Alternative t) => Apart t raw value -> Cofree t value
inmemory (Apart (x :< Ready xs)) = (:<) x $ inmemory . Apart <$> xs
inmemory (Apart (x :< Converted _)) = x :< empty