
You don't even need a type class, a simple data type is enough.
Very true, but I disagree that you've made it functional in any way, IO is all about sequencing things, it's very much not a functional style
data Engine = Engine { foo :: IO (), bar :: String -> IO () }
run e = processCommand e =<< getLine
processCommand e c | "foo" `isPrefixOf` c = foo e >> run e | "bar" `isPrefixOf` c = bar e c >> run e | otherwise = return ()
This is much nicer done as functions from String -> String, it becomes much more compassable, removes a sequential style from your code and stops processCommand depending on always working with the "run" function making it a bit more orthogonal. data Engine = Engine {foo :: String, bar :: String -> String} run e = unlines . map (proccesCommand e) . lines processCommand e c | "foo" `isPrefixOf` c = foo e | "bar" `isPrefixOf` c = bar e c | otherwise = "" Bob