{-|
  A simple embedded language for input/output. Shallow embedding.
-}
module Program.Shallow
  ( Input, Output
  , Program
  , putC, getC
  , run
  ) where

type Input  = String
type Output = String

-- | Shallow embedding: programs are represented by their semantics.
--   In this case a program is a function from the input to the result,
--   the remaining input and the output.
newtype Program a = P { unP :: Input -> (a, Input, Output) }

-- | Print a character.
putC :: Char -> Program ()
putC c = P $ \i -> ((), i, [c])

-- | Read a character (if there is one).
getC :: Program (Maybe Char)
getC = P $ \i -> case i of
  []     -> (Nothing, [], [])
  c : i' -> (Just c, i', [])

-- Program is a monad, which provides us with a nice interface for
-- sequencing programs.
instance Monad Program where
  return x = P $ \i -> (x, i, [])
  P f >>= k = P $ \i ->
    let (x, i1, o1) = f i
        (y, i2, o2) = unP (k x) i1
    in  (y, i2, o1 ++ o2)

-- | Running a program is simply returning its semantics.
run :: Program a -> Input -> (a, Input, Output)
run (P f) i = f i