{-# language DeriveFunctor #-} import Control.Monad data IOpure a = Return a | Output String (IOpure a) | Input (String -> IOpure a) deriving Functor instance Applicative IOpure where pure = Return (<*>) = ap instance Monad IOpure where Return x >>= f = f x Output s io >>= f = Output s (io >>= f) Input k >>= f = Input (\s -> k s >>= f) class Monad m => Interaction m where getInput :: m String produceOutput :: String -> m () instance Interaction IOpure where getInput = Input Return produceOutput x = Output x (Return ()) echoingUser :: IOpure a -> a echoingUser = go "no output" where go _ (Return x) = x go _ (Output o io) = go o io go o (Input k) = go o (k o) interactiveProgram :: Interaction m => m (String, String) interactiveProgram = do produceOutput "Jeff" name <- getInput produceOutput "Bob" name2 <- getInput return (name, name2) main :: IO () main = print (echoingUser interactiveProgram)