content: -- | Internal monad for the resolution algorithm
  --
  --  - we keep some state for the list of created values
  --  - we collect the applied functions as "Operations"
  --  - we might exit with a Left value if we can't build a value
  module Data.Registry.Internal.Stack where
  
  import Data.Registry.Internal.Statistics
  import Data.Registry.Internal.Types
  import Protolude
  
  -- | Monadic stack for the resolution algorithm
  type Stack a = StateT Statistics (Either Text) a
  
  -- | Return a value from the Stack if possible
  runStack :: Stack a -> Either Text a
  runStack = runStackWithValues mempty
  
  -- | Return a value from the Stack if possible
  runStackWithValues :: Values -> Stack a -> Either Text a
  runStackWithValues vs sa = evalStateT sa (initStatistics vs)
  
  -- | Run the stack to get a list of created values
  execStack :: Stack a -> Either Text Values
  execStack = execStackWithValues mempty
  
  -- | Return the state of the stack after executing the action
  --   This returns the list of built values
  execStackWithValues :: Values -> Stack a -> Either Text Values
  execStackWithValues vs sa = values <$> execStateT sa (initStatistics vs)
  
  -- | Return the list of applied functions after resolution
  evalStack :: Stack a -> Either Text Statistics
  evalStack = evalStackWithValues mempty
  
  -- | Run the stack to get a the statistics, starting with some initially created values
  evalStackWithValues :: Values -> Stack a -> Either Text Statistics
  evalStackWithValues vs sa = execStateT sa (initStatistics vs)
  
  -- | Get the current list of values
  getValues :: Stack Values
  getValues = values <$> get
  
  -- | Get the current list of operations
  getOperations :: Stack Operations
  getOperations = operations <$> get
  
  -- | Modify the current list of values
  modifyValues :: (Values -> Values) -> Stack ()
  modifyValues f = modifyStatistics (\s -> s {values = f (values s)})
  
  -- | Modify the current operations
  modifyOperations :: (Operations -> Operations) -> Stack ()
  modifyOperations f = modifyStatistics (\s -> s {operations = f (operations s)})
  
  -- | Modify the current statistics
  modifyStatistics :: (Statistics -> Statistics) -> Stack ()
  modifyStatistics = modify
  
  -- | Store a function application in the list of operations
  functionApplied :: Value -> [Value] -> Stack ()
  functionApplied output inputs = modifyOperations (AppliedFunction output inputs :)
  