Yet Another Monad Tutorial

Derek Elkins ddarius@hotpop.com
Tue, 12 Aug 2003 18:09:12 -0400


On Tue, 12 Aug 2003 23:15:30 +0200
Wolfgang Jeltsch <wolfgang@jeltsch.net> wrote:

> On Tuesday, 2003-08-12, 18:20, CEST, Jeff Newbern wrote:
> > [...]
> 
> > In the section "No Way Out":
> > ----------
> > The IO monad is a familiar example of a one-way monad in Haskell.
> > Because you can't escape from the IO monad, it is impossible to
> > write a function that does a computation in the IO monad but returns
> > a non-monadic value. Not only are functions of the type IO a -> a
> > impossible to create, but any function whose result type does not
> > contain the IO type constructor is guaranteed not to use the IO
> > monad. Other monads, such as List and Maybe, do allow values out of
> > the monad. So it is possible to write non-monadic functions that
> > internally do computations in those monads. The one-way nature of
> > the IO monad also has consequences when combining monads, a topic
> > that is discussed in part III.
> 
> This sounds rather vague to me. I don't like the formulation of a
> function doing a computation in the IO monad because execution of I/O
> actions is done separated from function application and evaluation
> (although interleaved with it because of lazy evaluation etc.). Tell
> me, if I'm pedantic.
> 
> By the way, it *is* possible to create a function of type IO a -> a:
>     f :: IO a -> a
>     f = const undefined

or just, f = undefined. This -would- be being pedantic and confusing.

> That's why in my last mail I spoke about *useful* functions which are 
> implementable for State (but not for IO).
> 
> > [...]
> 
> > The functions exported from the IO module do not perform I/O
> > themselves.
> 
> This is what I wanted to say above.
> 
> > They return I/O actions, which describe an I/O operation to be
> > performed. The I/O actions are combined within the IO monad (in a
> > purely functional manner) to create more complex I/O actions,
> > resulting in the final I/O action that is the main value of the
> > program.
> 
> Very good!
> 
> > The result of the Haskell compiler is an executable function
> > incorporating the main I/O action. Executing the program "applies"
> > this ultimate I/O action to the outside world to produce a new state
> > of the world.
> 
> I wouldn't talk about functions and function applications here because
> what you mean are not Haskell functions and applications of them. You
> can think of an I/O action being a function conceptually and the
> execution of it being a function application but using these terms
> here will most likely confuse readers.

Perhaps it would simply be best to just come out and describe the IO
monad as a state transformer for the world (i.e. World -> (a,World)). 
Then explain that a function IO a -> a would have to make up a World to
work, and obviously that's not meaningfully possible.  With this
interpretation (which is used by at least GHC), it's obvious to see
that the output of a Haskell compiler (i.e. main) is a function from the
World to a new World.  Unfortunately, this description relies on
knowledge of the State monad, so it may be best left to the section
dedicated to the IO monad.

On a side issue, throughout the tutorial you use the word "computation"
to describe monadic "functions", which happens to be the word I prefer;
however, here you use IO "action" rather than "computation".  There
doesn't seem to be much of a reason to change terminology here.  I'm
comfortable with both terms, and don't think it would cause much trouble
for a beginner, but it does seem arbitrary.

This also makes me think that at some point (I don't think you do this,
but I may just not remember) you may want to make explicit the dichotomy
in terminology. I.e. a remark along the lines of "throughout this
tutorial we we'll use the term computation for monadic values", you
could then for the IO section state "IO computations are also called IO
actions" if you change it, or "an IO action is just another name for IO
computation" if you continue using IO action.