{-# LANGUAGE GADTs #-}
{-|
  A simple embedded language for input/output. Deep embedding.
-}
module Program.Deep1 where

type Input = String
type Output = String

-- | The trivial deep embedding. We need to use a GADT to be allowed
--   the specific types on 'Get' and 'Put'.
data Program a where
  Put    :: Char -> Program ()
  Get    :: Program (Maybe Char)
  Return :: a -> Program a
  (:>>=) :: Program a -> (a -> Program b) -> Program b

-- | run function: translate syntax to semantics (Meaning as builtin Haskell)
run :: Program a -> Input -> (a, Input, Output)
run (Put c)    i       = ((), i, [c])
run Get        []      = (Nothing, [], [])
run Get        (c : i) = (Just c, i, [])
run (Return x) i       = (x, i, [])
run (p :>>= f) i       =
  let (x, i1, o1) = run p i
      (y, i2, o2) = run (f x) i1
  in  (y, i2, o1 ++ o2) 

instance Monad Program where
  return = Return
  (>>=)  = (:>>=)

-- | Output a character.
putC :: Char -> Program ()
putC = Put

-- | Input a character.
getC :: Program (Maybe Char)
getC = Get