[Haskell-cafe] Fighting the monad stack, MonadIO

Ryan Ingram ryani.spam at gmail.com
Thu Apr 10 21:06:18 EDT 2008

On 4/10/08, Adam Smyczek <adam.smyczek at gmail.com> wrote:
> If yes, is this a general concept/pattern
> how to hide functionality of a underlying monad,
> in this case hide IO entirely?

Yes, that's correct, although with IO you can't hide it entirely;
eventually you need a way to actually run the computation, and if
that's built on IO there's no way to do that without at least a way to
get -back- to the IO Monad.

On the other hand, you can use this to encapsulate "sandboxed" computations:

> module Console (Console, execConsole, consoleGetLine, consolePutLine)
> where

> newtype Console a = MkConsole { execConsole :: IO a }
>   deriving (Monad, Functor)

> consoleGetLine :: Console String
> consoleGetLine = MkConsole getLine

> consolePutLine :: String -> Console ()
> consolePutLine = MkConsole . putStrLn

MkConsole is a private constructor not exported from this module, so
the only way to construct one is via the operations we provide and the
monad/functor operations.  So we can prove that these operations never
do any network access, or file I/O, or weird pointer access.

Of course, with unsafeCoerce# and/or unsafePerformIO, client code can
break either/both of these claims:

> runConsole :: Console a -> a
> runConsole = unsafePerformIO . execConsole

> instance MonadIO Console where
>    liftIO = unsafeCoerce#
>    -- works because newtype is guaranteed not to change
>    -- the runtime representation

  -- ryan

More information about the Haskell-Cafe mailing list