forcing IO operations

Jay Cox
Wed, 20 Feb 2002 18:58:59 -0600 (CST)

On Wed, 20 Feb 2002, Andre W B Furtado wrote:

> hi, you sent me "blank" email (nothing besides my own message). Were you
> trying to tell something? :)
> -- Andre

Heh, actually I was.

I curse the day that the designers of pine put (control-X) for sending
a letter next to (control-C) for killing it :)

Please don't take offense to this, but do you feel unsafePerformIO
is really necessary for your application?

There's got to be another way...

In Systematic Design of Monads [1] there is given a systematic method to
wrap monads around other monads. I was going to email you an example
monad which wraps around the IO monad.  Granted, its a state monad wrapped
around the IO monad, which may not be of much use except for
organizational purposes.

I've essentially stolen the design from the aforementioned paper, but
since the paper appears to be for Gofer, the Haskell precursor, I decided
to modernize it a bit.

By the way, seems like there might be something along the lines of this
in the hugs library.  (If not, I'm sure I remember something about reader
and writer monad wrappers)

Jay Cox

[1]written by John Hughes & Magnus Carlson (1996)
Appologies to both since I cant give a more specific reference to the
paper. John (or Magnus), you wouldn't have happened to taken the time to
have updated the code from that paper, have you?  If you already have, I
appologize for not refering to it and using it as well :)


If you cut and paste the following lines I think they ought to be able to
run as in a haskell.lhs source file.

You proposed using some runMyMonad function.  in order to
combine IO actions with your code the runction returns and IO (blah).
see, the IO is hidden within the new monad but IO actions are still being
threaded through MyMonad.

>newtype MyNewStateMonad m s a = StateMonad (s -> m (a,s))
>bindST (StateMonad x) f =
>   StateMonad (\s -> ( x s >>= \(v,s') -> let StateMonad g = f v in g s'))
>unitST v = StateMonad (\s -> return (v,s))
>instance Monad m => Monad( MyNewStateMonad m s) where
>  (>>=) = bindST
>  return = unitST
>type MyMonad a = MyNewStateMonad IO Int a
>runMyMonad :: MyMonad a -> Int ->IO (a,Int)  -- (a,Int) the state tuple
>runMyMonad (StateMonad f) = f

that's it!
the remainder is mostly example code

1. lifts print and getLine to monadic actions under MyMonad (aka
MyNewStateMonad IO Int)

2. has an example monadic action (add50toMyMonadState) which has
absoultely nothing to do with the underlying IO monad.

3. lifted a sequence of IO actions to be executed under MyMonad via

4. combines all the actions under MyMonad into monad_actions so that
they may be run with runMyMonad.

note the line:
y<- runMyMonad monad_actions 0

the 0 initializes the state of the MyMonad,  (hence this is why runMyMonad
is typed MyMonad a -> Int ->IO (a,Int) )

>liftIOtoMyMonad'     :: (a -> IO ()) -> a -> MyMonad ()
>liftIOtoMyMonad' p q =
>     StateMonad $ \s -> do p q
>                           return ((),s)
>liftIOtoMyMonad      :: IO a -> MyMonad a
>liftIOtoMyMonad p    =
>     StateMonad $ \s -> do y <- p
>                           return (y,s)
>liftIOtoMyMonad_     :: IO () -> MyMonad ()
>liftIOtoMyMonad_ m   = liftIOtoMyMonad' (const m) ()
>getLineMM = liftIOtoMyMonad getLine
>printMM = liftIOtoMyMonad' print
>arbitrarysequence =
>    liftIOtoMyMonad $
>           do print "What's your name?"
>              name <-getLine
>              print $ "Your name is " ++ name
>add50toMyMonadState :: MyMonad ()
>add50toMyMonadState = StateMonad(\s -> return ((),50+s))
>monad_actions=   do arbitrarysequence
>                    printMM "What's your password"
>                    pass <- getLineMM
>                    printMM ("Your password is: " ++ pass)
>                    add50toMyMonadState
>                    return 3
>main = do y<- runMyMonad monad_actions 0
>          print $ "State:" ++ show (snd y) ++ " Value:" ++ show (fst y)

If you are reading this you should be  instead copying it to some file
and playing with it instead!