{-|
  A simple embedded language for input/output. Deep embedding.
-}
module Program.Deep2 where

type Input = String
type Output = String

-- Other approach...
data Program a = PutBind Char (Program a)          
               | GetBind (Maybe Char -> Program a) 
               | Return a

-- It turns out that bind can still be defined!
instance Monad Program where
  return = Return
  PutBind c p >>= k = PutBind c (p >>= k)
  GetBind f   >>= k = GetBind $ \c -> f c >>= k
  Return x    >>= k = k x


{- I pulled the above out of my hat, but...
   We can *calculate* the correct definition of (>>=) using
   the follwing intuitive meaning of PutBind and GetBind

    @PutBind c p == putC c >> p@
    @GetBind f   == getC >>= f@

 and the monad laws:

    Law 1.  return x >>= f   ==  f x

    Law 2.  m >>= return     ==  m

    Law 3.  (m >>= f) >>= g  ==  m >>= (\x -> f x >>= g)

   For instance,

      GetBind f >>= k             { meaning of GetBind }
  ==  (getC >>= f) >>= k          { third monad law }
  ==  getC >>= (\c -> f c >>= k)  { meaning of GetBind }
  ==  GetBind (\c -> f c >>= k)

-}

-- | Output a character.
putC :: Char -> Program ()
putC c = PutBind c $ Return ()

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

-- | The run function is easier than before
run :: Program a -> Input -> (a, Input, Output)
run (PutBind c p) i     = (x, i', c : o)
  where (x, i', o) = run p i
run (GetBind f) []      = run (f Nothing) []
run (GetBind f) (c : i) = run (f $ Just c) i
run (Return x)  i       = (x, i, [])