[Haskell-beginners] Is there a simpler way? Building a monad on `IO' Monad

Arlen Cuss celtic at sairyx.org
Sun Jan 9 11:47:40 CET 2011


Hi all,

Thanks for previous help on this list! I really appreciate it.

Today I wrote a monad and I'm not sure if I took a complicated way about
it. It's essentially a State monad, except with some specialised
functions that operate on the `state' if you will (though the state is
rarely mutated)—you initialise it with two Handles (e.g. stdin, stdout),
and then a set of specialised functions defined `within' the monad will
operate on those handles. It saves you from passing the Handles
throughout the functions and subcalls.

It's quite possible there already exists a monad for this job, or that
IO will actually let you do this, but I didn't find it in a bit of
searching, and concluded this would be a fun way to solve the problem.
If anyone has any advice on shortening the code, or possibly removing
the need for it, please let me know!

Here's the main monad:

> import System.IO
> import Control.Applicative
> 
> newtype IODirector a = IODirector { runIODirector :: (Handle,Handle)
-> IO (a, (Handle,Handle)) }
> 
> instance Monad IODirector where
>   return a = IODirector $ \hs -> return (a, hs)
>   m >>= k  = IODirector $ \hs -> do (a, hs) <- runIODirector m hs
>                                     runIODirector (k a) hs

This is basically the same as State, except we `return' to the IO monad,
as is the result of the stateful computation an I/O action, IO (a,
(Handle,Handle)).

We then have a type-class for the actual I/O we can perform within the
monad:

> class MonadDirectedIO a where
>   dPutStr :: String -> a ()
>   dPutStrLn :: String -> a ()
> 
>   dGetLine :: a String
>   dGetChar :: a Char
...

The functions here continue for all the ones from IO I really wanted to
use, and the instance is not surprising:

> instance MonadDirectedIO IODirector where
>   dPutStr s = IODirector $ \hs@(_,hOut) -> do hPutStr hOut s
>                                               return ((), hs)
>   dPutStrLn = dPutStr . (++ "\n")
> 
>   dGetLine = IODirector $ \hs@(hIn,_) -> do r <- hGetLine hIn
>                                             return (r, hs)
>   dGetChar = IODirector $ \hs@(hIn,_) -> do r <- hGetChar hIn
>                                             return (r, hs)

I'm aware I didn't have to put this in a type-class, but it seemed a
reasonable thing to do.

There was a little plumbing work to `enclose' IO within this monad. My
question is - did I do it right? Or is there a simpler way?

Of course, if there's already such functionality in the built-in, I'd
also be interested to hear ... ;-)

Cheers!

Arlen
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: This is a digitally signed message part
URL: <http://www.haskell.org/pipermail/beginners/attachments/20110109/94c42b4a/attachment.pgp>


More information about the Beginners mailing list