Brian Hulley brianh at metamilk.com
Sat May 27 17:06:06 EDT 2006

```Matthew Bromberg wrote:
> That's an interesting statement that bears further scrutiny.  I've
> been viewing monads as a kind of encapsulation in a quasi-hidden
> world state.

The IO monad can be viewed as encapsulating a function from a
world state to a pair consisting of an updated world state and a "return
value" as follows:

data IO a = IO (RealWorld -> (RealWorld, a))

> Yes a monad is a function that would give you an output
> Simon Peyton Jones' tutorial.  I've been thinking of actions in terms
> of the functions x -> IO(a), but Simon calls these IO actions and
> calls monads actions as you do.

So the function is contained *inside* the representation of the IO action so
it's IO (x -> (x,a)) not x -> IO a

>
> Is it your claim that whenever an 'IO action' is performed on
> something like  IO (x)  with x <- newListArray a l
> that the newListArray function will be called ?

If I write:

do
let n = newListArray a l
p <- n
q <- n

two separate arrays will be created, because n is the action of creating a
new array, and this action is executed twice in the body of the do.

do
x <- newListArray a l
let r = return x
p <- r
q <- r

only one array would be created, and this would be shared between x, p, and
q, because the action (newListArray a l) is now only executed once (x is the
result of the action ie the array, not the action itself)

>  So with the old
> paradigm, where Rmatrix was stored as
> (Int, Int, IO(StorableArry Int CDouble)).  A typical matrix operation,
> that calls out to a BLAS C routine cside_effect()  looks something
> like this under the old scheme
>
> matfunc A = (u,v, arr) where
>     u = f1  (getrows A)  (getcols B)
>     v = f2  (getrows A)  (getcols B)
>    arr = do
>       arrA <- getarr A
>       withStorableArray arrA  (\vara -> cside_effect vara )
>       return arrA
>
> Now when one uses this code
> do
>    A <- getAfromSomewhere
>   fA = matfunc A
>   B <- anotherfunc A
>
> fA has been changed by the cside_effect function, but A has not!  Is
> it your contention that the array in A is essentially copied or
> created anew for every getarr A call?

matfunc A never actually calls getarr A. It just sets up a tuple where the
3rd element is an IO action that, when that 3rd element is actually
executed, would execute the 3rd element of the original A, and pass the
array that was returned by it to your C function, and then return that
array.

I think the essence of the confusion is that there are two kinds of actions
that the 3rd element of A might be bound to:

1) An action which just returns an array that was previously created
2) An action which creates a new array and returns that

And both of these have the same type!!! because all the type system knows is
that these two actions "do something (maybe nothing) then return an array"

Case 2 was what you were doing right at the beginning, when the 3rd element
was (newListArray a l), and case 1 was what I pointed out you could have
used ie using the action (return arr) where arr was allocated during the
construction of Rmatrix.

> I think getarr A looked
> something like
> getarr (Rmatrix (r,c,arr)) =  arr
>
> in the old technique, but now looks like
>
> getarr (Rmatrix (r,c,arr)) =  return arr
>
> Is this perhaps an effect of lazy evaluation?  When does one actually
> need to evaluate the constructor for the storable array contained in
> A? Hmmm. Is it that the rules specify that an IO action forces the
> evaluation of the value in the monad, but otherwise this value may be
> unevaluated?

No. An action, when executed, returns a value, but this doesn't imply that
all actions have values stored in them.

Bulat's illustration of getChar is a perfect example of this, since getChar
has type IO Char but does not store the Char inside it, and executing it
multiple times will in general give different characters back depending on

However the action (return 'q') also has type IO Char but this time, the 'q'
is stored inside the action (return 'q').

So it matters exactly what action it is, since they can't be distinguished
just by looking at the types.

> So    return x  doesn't evaluate x but  (return x) >> =   \z -> IOfunc z
> does?

If you executed (return x), what first happens is that the *function*
return:: a-> IO a is applied to its argument value 'x' to get an *action*
which, when executed, will be able to supply the result value x, then this
action *is* executed, and x is available as the result.
Depending on the particular monad, x may or may not be evaluated. AFAIK for
the IO monad, it would not be, but I think it is possible to conceive of a
monad where the return function would need to evaluate its argument.

Also, even if x was unevaluated by it's journey through the return function
and executed action which hands it onto the input of iofunc, iofunc may or
may not evaluate it.

The short answer is: lazyness is a separate issue.
A longer answer (given near the end of the thread referenced below) is that
it depends on the fact that evaluation never happens under a lambda ie \x ->
exp prevents exp being looked at until the function is applied, and that the
sequence operator >> is defined in terms of >>= so that the second action is
always lifted into a function.

>
> This would actually make sense in the end.  The IO action of
> A <- getAfromSomewhere would not evaluate the monad that is the third
> element of the tuple A, since there is no real 'need' to do so.  So in
> fact no constructor of A's array would ever get evaluated until a
> function was applied to it, which would have to be in the form of an
> IO action.  That's a nice mind twister.  Clean's uniqueness types are
> a little easier to grasp I think.  Ahh I think I understand now.  It
> has to work that way or else you cannot guarantee the sequential
> execution property of monads.  Thus if  y :: IO (a) and you evaluate
>   z <- func (f1 y) (f2 y),  the evaluation order of y is controlled by
> what is implemented in func (and f1 and f2),  not by evaluation rules
> for arguments. The value wrapped in y MUST remain unevaluated until IO
> 'actions' are performed on it.

You're *very* close to understanding it. You're right that in Haskell
arguments are never evaluated eagerly by some generic evaluation rule - the
function application is just replaced by the body of the function with that
unevaluated argument expression replacing all occurrences of the argument
variable ie lazy evaluation.

You just need to distinguish between the concepts of evaluation and
execution, so in your example above, where y has been bound to some
expression that evaluates to an action, depending on func, f1, f2, the
expression y is bound to may be evaluated zero or more times (depending on
whether or not y is used polymorphically within the function(s)), and the
resulting action(s) may be executed multiple times.

Personally I found it easier to underastand monads by looking at a simple
state monad first so it's very clear what is happening, because then you can
look inside the implementation of >>= and return and see that these are just
quite simple functions. It is impossible to understand monads just by
considering the types alone.

The IO monad is extremely tricky to fully understand because you can't see
how it is implemented, and as explained above, the types are not enough to
distinguish between quite different behaviours whereas a state monad reveals
exactly what's happening.

(There was a long discussion beginning
how best to understand IO but certainly before trying to do so, a state
monad is *infinitely* easier to understand and then provides a good basis
for understanding IO)

The first monad I understood was the state monad on page 261 of Paul Hudak's
book "The Haskell School of Expression", and there are also some tutorials
on the wiki at

Regards, Brian.

--
Logic empowers us and Love gives us purpose.
But societal laws, and religious dogma,