--------------------------------------------------------------------------------
-- Deconstruction
--------------------------------------------------------------------------------

--- | Convert a 'Machine' back into a 'Plan'. The first value the
--- machine yields that is tagged with the 'Left' data constructor is
--- used as the return value of the resultant 'Plan'. Machine-yielded
--- values tagged with 'Right' are yielded -- sans tag -- by the
--- result 'Plan'. This may be used when monadic binding of results is
--- required.
deconstruct :: Monad m => MachineT m k (Either a o) -> PlanT k o m a
deconstruct m = PlanT $ \r y a f ->
  let aux k = runPlanT (deconstruct k) r y a f
  in runMachineT m >>= \v -> case v of
       Stop -> f
       Yield (Left o) _ -> r o
       Yield (Right o) k -> y o (aux k)
       Await g fk h -> a (aux . g) fk (aux h)

-- | Use a predicate to mark a yielded value as the terminal value of
-- this 'Machine'. This is useful in combination with 'deconstruct' to
-- combine 'Plan's.
deconstruct :: Monad m => MachineT m k (Either a o) -> PlanT k o m a
deconstruct m = PlanT $ \r y a f ->
  let aux k = runPlanT (deconstruct k) r y a f
  in runMachineT m >>= \v -> case v of
       Stop -> f
       Yield (Left o) _ -> r o
       Yield (Right o) k -> y o (aux k)
       Await g fk h -> a (aux . g) fk (aux h)

-- | Use a predicate to mark a yielded value as the terminal value of
-- this 'Machine'. This is useful in combination with 'deconstruct' to
-- combine 'Plan's.
deconstruct m = PlanT $ \r y a f ->
  let aux k = runPlanT (deconstruct k) r y a f
  in runMachineT m >>= \v -> case v of
       Stop -> f
       Yield (Left o) _ -> r o
       Yield (Right o) k -> y o (aux k)
       Await g fk h -> a (aux . g) fk (aux h)

-- | Use a predicate to mark a yielded value as the terminal value of
-- this 'Machine'. This is useful in combination with 'deconstruct' to
-- combine 'Plan's.