Based on an answer here I was inspired to try and make a program where the state monad could be swapped for the IO monad and it would still work. So far I came up with:
{-# LANGUAGE FlexibleInstances #-}
import Control.Monad.State
class Monad m => Interaction m where
getInput :: m String
produceOutput :: String -> m ()
instance Interaction IO where
getInput = getLine
produceOutput = putStrLn
instance Interaction (State String) where
getInput = get
produceOutput = put
interactiveProgram :: Interaction m => m ()
interactiveProgram = do
name <- getInput
produceOutput $ "Hey " ++ name
This works fine if I run it in GHCi, and I can also run interactiveProgram
like so: runState interactiveProgram "Jeff"
. It gets messy when I add extra getInput
calls though:
interactiveProgram :: Interaction m => m ()
interactiveProgram = do
name <- getInput
name2 <- getInput
produceOutput $ "Hey " ++ name ++ " and " ++ name2
In the case of the IO monad, the user is prompted for another name and the output is something like "Hey Jeff and Geoff". But in the state monad example, I have no way to provide that second name. Instead I get ((),"Hey Jeff and Jeff)
(the provided name repeated twice).
Is it possible to come up with an implementation for the State String
instance that allows arbitrarily many "inputs" that get fed to the getInput
calls?