From: kff@dentex.tds.kth.se (Karl-Filip Faxen)
Subject: Re: Resource control (was: Re: Why curry?)
Reply-To: kff@dentex.tds.kth.se (Karl-Filip Faxen)
Organization: Royal Institute of Technology, Stockholm, Sweden
Date: Tue, 13 Oct 1992 10:57:48 GMT

In article <TMB.92Oct12210548@arolla.idiap.ch>, tmb@arolla.idiap.ch (Thomas M. Breuel) writes:
|> There shouldn't be a division
|> into "compilation, profiling, optimizing, execution". 

I agree. Today we have rather a small number of "times"; compile-time,
link-time, run-time. At each of these "times" new information that can 
be used to optimize the execution becomes available. But we could have
many more such "times" during what is now lumped together as run-time.

To be precise, I think that there is a "time" for each strongly connected
component of the static call graph (some of these "times" of course occur
many times during the execution of a program since they are nested within
each other).

|> What I'm saying is that most people who know SML reasonably well
|> understand clearly how I want my computation to be carried out when I
|> write something like:
|> 
|>    fun f(x) =
|>        let
|>    	   val z = g(x)
|>        in
|>            fn y => h(y,z)
|>        end
|>    
|>    fun f'(x) = fn y => h(y,g(x))
|> 
|> In the first case, I don't want "g(x)" to be recomputed, in the second
|> case I do want it recomputed. It seems that this is annotation enough.
|> Why invent a new notation? 

Because I want the 'let' notation for other purposes (remember I am
generally using lazy languages). I use it when it makes my code clearer,
not to control execution. Hence it's important to me that it doesn't 
_mean_ anything, i.e. that it's referentially transparent.

Regarding the example itself, I suppose that the reason for you to 
sometimes want to recompute "g(x)" is that it has some side effect
that should occur? In a pure language, you don't need to care about
how many times an expression is evaluated, at least not for program
correctness. So the first version is always better in this case.

Incidentally, here is how you'd write the first version in Haskell
if your compiler implements fully lazy lambda lifting:

   f x y = h y (g x)

The fully lazy lambda lifter would then transform this to something
like the following:

   f x = f' (g x)

   f' z y = h y z

Then if you wanted to compute "map (f 17) xs", the "(f 17)" application
would be shared and as soon as it was used for the first time, it would
be rewritten to "(f' (g 17))" where "g 17" would also be shared and
therefore evaluated at most once.

Well, this was a bit of a sidetrack ... what I thought about was rather
annotations that would express the desired order of evaluation, more 
like writing "f (after (g x))" to indicate that "g x" should be
completely evaluated before "f" was called. Now, in this case "after"
is almost a hyperstrictness annotation, the difference being that it 
forced the compiler to use this schedule, wheras a hyperstrictness
annotation allowed the compiler to do as it thought best. Still other
annotations could be used to indicate the order of evaluation of various
subexpressions.

Cheers,
          Karl-Filip

------------------------------------------------------------
Make the frequent case fast, and the fast case frequent!
Newsgroups: comp.lang.functional