[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