> | Bj|rn von Sydow ([EMAIL PROTECTED]) reports that this expression
> | runs in constant space
> |   mapM_ putStr (repeat "")
> | but this program does not:
> |   main = mapM_ putStr (repeat "")
> | 
> | This is caused by "CAF-leaks" - a long-standing problem of most Haskell
> | compilers (except HBC?).

Mark P Jones <[EMAIL PROTECTED]> responds:
> I can see why you might be unhappy about this, but I should say that Gofer
> (on which Hugs is based) was specifically designed to behave in this way.
> It's interesting that Alastair specifically mentions compilers in the
> above because, in my view, the requirements change in the context of an
> interactive interpreter.  In particular, this feature gives a the same
> treatment of variable bindings at the top-level as it does to local
> definitions, it allows the construction of values that are memoized across
> a sequence of evaluations in a session, and has been quite heavily used by
> a number of people as a way of teaching certain concepts (particularly
> lazy evaluation).
> 
> I can see that t would be useful to have a way of running programs without
> the caching of top-level CAFs.  But I think it would be a great pity to
> loose this feature altogether.  At the very least, users should have a
> command line option so that they can choose which behaviour they need.

I should have been clearer about what the RTS will provide:

1) In a non-interactive environment, it will treat top level objects
   just the same as non-top-level objects:

   o They will get updated as they are evaluated.
   o They will get garbage collected when no longer required.

   Thus, main will get updated but, at the next GC, it will get
   deleted because it doesn't call itself and so is dead.

   Since we're going to be storing bytecodes on the heap, we'll also
   find that loading a large module out of which we only want one
   function won't be too bad - after the first garbage collection, all
   the unused functions will be deleted.

2) In an interactive environment, we'll provide a choice of behaviours:

   o By maintaining pointers to the top level objects in the code,
     we can prevent them from being garbage collected - the current
     behaviour.

   o By not keeping pointers to top level objects, we only hang onto
     updated objects if they could be used later in the current
     evaluation.
     (To avoid having to recompile each time, we _will_ hang onto
     the bytecode objects for top level objects - but these won't
     contain pointers to the updated objects so we'll avoid the 
     CAF leak.)


In a later message, Mark writes:
> [Bj|rn von Sydow <[EMAIL PROTECTED]> writes:]
> | From this is is not at all apparent to the user that
> | evaluating a top-level binding of type IO () will lead to constructing
> | and caching an object such as=20
> | 
> | main =3D putStr "" >> putStr "" >> putStr "" >> mapM_ putStr (repeat "")
> | 
> | corresponding to the view that >> is a constructor.=20
> 
> I disagree, and would use simple equational reasoning to justify it.
> The relevant, simplified clauses in the definitions would be:
> 
>    mapM_ f (x:xs) = f x >> mapM_ f xs
>    repeat x       = x : repeat x
> 
> And from these one can readily deduce that:
> 
>   mapM_ putStr (repeat "")
>     ===> mapM_ putStr ("" : repeat "")
>     ===> putStr "" >> mapM_ putStr (repeat "")
>     ===> putStr "" >> mapM_ putStr ("" : repeat "")
>     ===> putStr "" >> putStr "" >> mapM_ putStr (repeat "")
>     ===> putStr "" >> putStr "" >> mapM_ putStr ("" : repeat "")
>     ===> putStr "" >> putStr "" >> putStr "" >> mapM_ putStr (repeat "")
>     ===> etc...

Begin quibble.

  You're not using "equational reasoning" here - equational reasoning
  has nothing to say on the subject of caching results for later use so
  you can't use it to argue about whether or not caching is happening.

  If you were using equational reasoning, you'd probably have used "=="
  or "=" instead of "===>" in the above reduction sequence.

End quibble.

Incidentally, the Haskell report doesn't seem to require lazy
evaluation --- as far as I can tell, an implementation which used call
by name evaluation (with no updates) would be a valid implementation -
though I'm not sure how many people would use it.


Alastair


Reply via email to