Last post we looked at using a less general form of the Free monad to purely represent side-effects in F#. Because Haskell supports higher-order polymorphism it makes using this approach much easier. Here is the complete example from that post, rewritten in Haskell:
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Free
data Terminal a =
WriteLine String a
| ReadLine (String -> a)
deriving (Functor)
writeLine :: String -> Free Terminal ()
writeLine s = liftF $ WriteLine s ()
readLine :: Free Terminal String
readLine = liftF $ ReadLine id
helloWorld :: Free Terminal ()
helloWorld = do
writeLine "Hi, what's your name?"
name <- readLine
writeLine $ "Hello " ++ name
interpretIO :: Free Terminal a -> IO a
interpretIO (Free (WriteLine s a)) = putStrLn s >> interpretIO a
interpretIO (Free (ReadLine f)) = getLine >>= interpretIO . f
interpretIO (Pure a) = return a
This is using Free
and liftF
from Control.Monad.Free, but for the sake of equivalence with the F# example, here’s a definition that can be used in place of the import Control.Monad.Free
line in the above code.
data Free f a = Pure a | Free (f (Free f a))
instance Functor f => Monad (Free f) where
return = Pure
(Pure a) >>= f = f a
(Free fr) >>= f = Free (fmap (>>= f) fr)
liftF :: Functor f => f a -> Free f a
liftF = Free . fmap Pure