{-# LANGUAGE FunctionalDependencies, Safe #-}

{-|
Module      : Dep.Class.Walkable
Description : A module that exposes a type class that allows "walking" through a data structure.
Maintainer  : hapytexeu+gh@gmail.com
Stability   : experimental
Portability : POSIX

This module defines a typeclass 'Walkable' that specifies that one can walk through a deterministic
data structure for a given type of "steps".
-}

module Dep.Class.Walkable (
    -- * Deterministic steps and walks
    Walkable(step, walk, allStep, allWalk, stepValues, stepValues', allStepValues, allStepValues', walkValues', allWalkValues)
  ) where

import Dep.Utils(toList')

import Data.Foldable(toList)

-- | A typeclass that specifies that we can walk through a datastructure with steps.
class Walkable f step | f -> step where
  -- | Take one step with the given step parameter, and return an object with the same
  -- type.
  step
    :: f a  -- ^ The original object where we will make a step.
    -> step  -- ^ The given step we take on the given data structure.
    -> f a  -- ^ The result of an object when we take one step.
  step f a
item step
stp = f a -> [step] -> f a
forall (f :: * -> *) step (g :: * -> *) a.
(Walkable f step, Foldable g) =>
f a -> g step -> f a
walk f a
item [step
stp]

  -- | Obtain the values of the item after making a step.
  stepValues' :: Foldable f
    => f a -- ^ The initial state.
    -> step -- ^ The step that we take to move to another state.
    -> [a] -- ^ A list of tail elements that can be added to the result.
    -> [a] -- ^ The list of values from the modified state together with the given tail.
  stepValues' f a
item step
stp = f a -> [a] -> [a]
forall (f :: * -> *) a. Foldable f => f a -> [a] -> [a]
toList' (f a -> step -> f a
forall (f :: * -> *) step a. Walkable f step => f a -> step -> f a
step f a
item step
stp)

  -- | Obtain the values of the item after making a step.
  stepValues :: Foldable f
    => f a -- ^ The initial state.
    -> step -- ^ The step that we take to move to another state.
    -> [a] -- ^ The list of values from the modified state.
  stepValues f a
item step
stp = f a -> step -> [a] -> [a]
forall (f :: * -> *) step a.
(Walkable f step, Foldable f) =>
f a -> step -> [a] -> [a]
stepValues' f a
item step
stp []

  -- | Apply the same step for all the items in the given collection (functor) of items.
  allStep :: Functor g
     => g (f a)  -- ^ The collection of items on which we apply a step.
     -> step  -- ^ The given step that will be applied.
     -> g (f a) -- ^ A collection of items that are the result of making a step for each input item.
  allStep = (step -> g (f a) -> g (f a)) -> g (f a) -> step -> g (f a)
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((f a -> f a) -> g (f a) -> g (f a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((f a -> f a) -> g (f a) -> g (f a))
-> (step -> f a -> f a) -> step -> g (f a) -> g (f a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (f a -> step -> f a) -> step -> f a -> f a
forall a b c. (a -> b -> c) -> b -> a -> c
flip f a -> step -> f a
forall (f :: * -> *) step a. Walkable f step => f a -> step -> f a
step)

  -- | Obtain the values of the items after making the same step for all items.
  allStepValues' :: (Foldable f, Foldable g)
    => g (f a) -- ^ The initial states.
    -> step -- ^ The step that we take to move to another state.
    -> [a] -- ^ A list of tail elements that can be added to the result.
    -> [a] -- ^ The list of values from the modified state together with the given tail.
  allStepValues' g (f a)
items step
stp [a]
tl = (f a -> [a] -> [a]) -> [a] -> g (f a) -> [a]
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (f a -> [a] -> [a]
forall (f :: * -> *) a. Foldable f => f a -> [a] -> [a]
toList' (f a -> [a] -> [a]) -> (f a -> f a) -> f a -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (f a -> step -> f a
forall (f :: * -> *) step a. Walkable f step => f a -> step -> f a
`step` step
stp)) [a]
tl g (f a)
items

  -- | Obtain the values of the items after making the same step for all items.
  allStepValues :: (Foldable f, Foldable g)
    => g (f a) -- ^ The initial states.
    -> step -- ^ The step that we take to move to another state.
    -> [a] -- ^ The list of values from the modified state.
  allStepValues g (f a)
items step
stp = g (f a) -> step -> [a] -> [a]
forall (f :: * -> *) step (g :: * -> *) a.
(Walkable f step, Foldable f, Foldable g) =>
g (f a) -> step -> [a] -> [a]
allStepValues' g (f a)
items step
stp []

  -- | Take a sequence of steps with the given list of steps.
  walk :: Foldable g
    => f a  -- ^ The original object where we will make a step.
    -> g step  -- ^ The given foldable of steps we take on the given data structure.
    -> f a  -- ^ The result of an object when we take one step.
  walk = (f a -> step -> f a) -> f a -> g step -> f a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl f a -> step -> f a
forall (f :: * -> *) step a. Walkable f step => f a -> step -> f a
step

  -- | Take a sequence of steps with the given list of steps and return the values out of the target state.
  walkValues' :: (Foldable f, Foldable g)
    => f a  -- ^ The initial state for which we will make a walk.
    -> g step -- ^ A sequene of steps that describe the walk.
    -> [a] -- ^ A list of tail elements that can be added at the end of the list.
    -> [a] -- ^ The values found after making the a walk with the given 'Walkable' in the 'Foldable'.
  walkValues' f a
x = f a -> [a] -> [a]
forall (f :: * -> *) a. Foldable f => f a -> [a] -> [a]
toList' (f a -> [a] -> [a]) -> (g step -> f a) -> g step -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. f a -> g step -> f a
forall (f :: * -> *) step (g :: * -> *) a.
(Walkable f step, Foldable g) =>
f a -> g step -> f a
walk f a
x

  -- | Obtain all the values the 'Foldable' f spans after performing the
  -- same ('Foldable') of steps on all the given intial states.
  allWalkValues :: (Foldable f, Foldable g, Functor g, Foldable h)
    => g (f a)  -- ^ The given collection of initial states over which a fold is made.
    -> h step  -- ^ A 'Foldable' of steps that we will take for all states.
    -> [a] -- ^ The corresponding values that are wrapped in the target states.
  allWalkValues g (f a)
x = (f a -> [a]) -> g (f a) -> [a]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap f a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList (g (f a) -> [a]) -> (h step -> g (f a)) -> h step -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. g (f a) -> h step -> g (f a)
forall (f :: * -> *) step (g :: * -> *) (h :: * -> *) a.
(Walkable f step, Functor g, Foldable h) =>
g (f a) -> h step -> g (f a)
allWalk g (f a)
x

  -- Apply the same walk to all elements in the collection (functor).
  allWalk :: (Functor g, Foldable h)
    => g (f a) -- ^ The given collection of initial objects.
    -> h step -- ^ The given foldable of steps that we take.
    -> g (f a) -- ^ The result collection of items after applying the steps.
  allWalk = (g (f a) -> step -> g (f a)) -> g (f a) -> h step -> g (f a)
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl g (f a) -> step -> g (f a)
forall (f :: * -> *) step (g :: * -> *) a.
(Walkable f step, Functor g) =>
g (f a) -> step -> g (f a)
allStep
  {-# MINIMAL step | walk #-}