Alastair wrote:
> | It looks to me as though you've redicovered the importance of tail calls.

Koen replied:
> It is indead clear that this has to do with tail calls. But what is the
> solution? Is it possible to make a writer monad definition that allows
> functions like
> 
>   foo 0 = return ()
>   foo n =
>     do write "a"
>        x <- foo (n-1)
>        write "b"
>        return x
> 
> to run without space leak? Or are we forced to rewrite "foo"?
> 
> Note that the described behavior doesn't hold for _all_ monads. We can of
> course take for the definition of W:
> 
>   type W = IO
> 
>   write = putStr
> 
> And then (at least in Hugs) there is no space leak.
> But this is not what you want, because now we are forced to have "foo"
> only on toplevel...

I just tried this on Hugs.  I was expecting it to run out of stack space
but, in fact, it runs out of heap space first.  So your counterexample
turns out to be an example :-)

It was clear it was going to do one or the other because it's not tail
recursive.  On some systems such as GHC, I expect you'll run out of stack
first, on Hugs you happen to run out of heap first.  You can trade stack
space for heap space but whichever you run out of, you're still running
out of space.

I can't think of any non-degenerate monad for which this program
 will run in constant space. For any useful monad, it's clear that
 it must use (at least) O(n) space because it has to somehow remember to 
 write "b" as many times as it writes "a".  Standard results tell us
 that this is beyond a finite state machine (ie something that runs in 
 constant space).  (You could imagine rewriting this particular program
 to use a tail recursive function to print out n "a"s then another tail
 recursive function to print out n "b"s - but I don't expect a compiler
 to do this for me and I don't think it's possible to write a non-trivial 
 monad which somehow accomplished this either.)

In summary: 

  Tail calls are the problem here.

I don't believe there's anything deeper to look for - you just have to 
write your monadic code in a tail recursive style.

I believe the problem applies to all non-trivial monads.


Alastair Reid, Yale Haskell Project

ps I didn't define the term "trivial monad" - let me give example:
   The constant monad M_c where

   return x = c
   m >>= k = c

  is certainly trivial - there are so many identities in this 
  monad that every program of this type can be run in constant
  space.  (Moggi has explored more formal definitions of triviality -
  these might be applicable here.)

pps None of the above answers the original question which was:

    Is it possible to write a monad such that this equality holds
    both denotationally and operationally

      do {x <- foo; return x} = foo

    For all I know, the answer to this question is 
    "yes, with sufficient sneakiness".




Reply via email to