caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] laziness
@ 2004-09-04  6:30 skaller
  2004-09-04  8:40 ` Marcin 'Qrczak' Kowalczyk
  2004-09-05 10:50 ` Jon Harrop
  0 siblings, 2 replies; 23+ messages in thread
From: skaller @ 2004-09-04  6:30 UTC (permalink / raw)
  To: caml-list

Since I've been fiddling with various optimisations in
my own compiler, I've become quite curious about
laziness and its relation to both performance and semantic
guarrantees.

As motivation consider:

	let f c a b = if c then a else b

As I understand it Ocaml will handle a call

	f c' a' b'

by evaluating f,c',a',b' in some order then
applying f to c' a' b' -- eager evaluation.
However if the call is *inlined* to get

	if c' then a' else b'

then perhaps a' or b' will never be evaluated.

[As I understand it, inlining is substitution aka
normal order evaluation for lambda terms; 
that is, a lazy evaluation strategy.]

In particular my 'impression' that ocaml evaluates
function arguments eagerly would be wrong.

It seems that procedural code (which includes 
functional expressions) is actually a way to
specify evaluation order and timing and typically
control flow is used to delay evaluation -- so that
procedural programming is actually quite lazy.

OTOH procedural languages (including Ocaml) also
allow early evaluation.

>From a performance viewpoint, both eager and lazy
evaluation have advantages -- lazy evaluation avoids
gratuitously evaluating a term which the result does
not depend on, whereas eager evaluation can be used to
prevent an evaluation being done twice. Hence procedural
languages like Ocaml can be very fast because you can
hand tune the tradeoffs (also making it harder to 
reason about the outcome .. )

There seems to be some kind of sliding scale like this
for functional code:

C/C++ --> sequence points (functions have side effects)
Ocaml --> looser assurances
Felix --> no side effects but not fully parametric
Haskell --> everything is lazy, immutable, and parametric

So I have two questions:

(1) exactly what does Ocaml guarrantee?

(2) what kind of performance and semantic
tradeoffs are involved with perturbations
on these assurances?

What I'm actually finding at the moment with Felix is that
my optimisation efforts, mainly inlining, are actually
'lazifying' code, and thus involve a change in semantics --
although Felix functions can't have side effects, there
are also procedures which can, and functions may depend
on variables which procedures modify, so that in general
the result of a function does depend on when it is
executed. This effect is also available in Ocaml via
references.

I used to think the difference between lazy and eager
evaluation were minor quirks -- I'm tending to think now
the difference is quite fundamental, eg a purely functional
language is necessarily lazy, because lazy evaluation
is mandatory -- an eager evaluation strategy *requires* 
procedural constructions to provide the laziness, and so
can't work with a purely functional language.

Any comments would be appreciated.

-- 
John Skaller, mailto:skaller@users.sf.net
voice: 061-2-9660-0850, 
snail: PO BOX 401 Glebe NSW 2037 Australia
Checkout the Felix programming language http://felix.sf.net



-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 23+ messages in thread
* RE: [Caml-list] laziness
@ 2004-09-05  1:07 Jason Smith
  2004-09-05  5:46 ` skaller
  2004-09-06  0:57 ` Richard Jones
  0 siblings, 2 replies; 23+ messages in thread
From: Jason Smith @ 2004-09-05  1:07 UTC (permalink / raw)
  To: caml-list, skaller

>> > However if the call is *inlined* to get
>> >
>> > 	if c' then a' else b'
>> >
>> > then perhaps a' or b' will never be evaluated.
>I understand that argument -- but that doesn't mean the
>compiler conforms to the specification, nor that the
>specification is best.

I'm not sure about OCaml but in GHC we could garuentee that the arguments a' 
and b' would be reduced using the 'case' syntax in GHC's intermediate language 
"Core". This the code would become something like this:

case c' of 
{ c'' ->
   case a' of
   { a'' ->
      case b' of
      { b'' -> if c'' then a'' else b'';
      }
   }
}

case forces evaluation to WHNF form, thereby garuenteeing correct eager order 
evalation. I presume O'Caml would do the same to keep side-effects evaluting 
correctly.

>> E.g. the order of evaluation of arguments is
>> unspecified, so it might be different depending on inlining; but
>> OCaml does specify that each argument are evaluated exactly once
>> and inlining doesn't change that.
>
>Must they be evaluated before the function is called?

If O'Caml follows the usual eager order semantics then evaluation is AOR or 
Applicative order redection. (Which is what I use in Mondrian).

There are several locations where we may attempt to reduce this overhead.

Strictness analysis: Determines usuing some form of abstract reduction 
semantics weather the argument is "strict" in the function, i.e. that it will 
be used at all. If it isn't then there is no need to reduce it. The problem 
with this is that in O'Caml u once again have side-effect's raise there ugly 
head. This means that even if the argument is not used in the function, the 
results of evaluating the argument which uses side-effects is. So you may have 
to analyse the argument itself and see if it uses any reference semantics.

Usage analysis: Determines "how many times" an argument is used. Not so much 
applicable to eager order evaluation but very handy in lazy languages. We can 
save the cost of a THUNK update by determining if we actually need to update 
the thunk with the new value once the argument has been reduced.

There are a range of other analysis techniques that "increase the degree of 
laziness" aka full-laziness transformation, reduce the cost of creating 
intermediate data structures aka deforestation, let-floating etc.. refer to a 
great body of literature for comments on these area's.

HTH
Jason.

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 23+ messages in thread
* RE: [Caml-list] laziness
@ 2004-09-06  9:07 Jason Smith
  2004-09-06 10:18 ` skaller
  0 siblings, 1 reply; 23+ messages in thread
From: Jason Smith @ 2004-09-06  9:07 UTC (permalink / raw)
  To: Richard Jones, skaller; +Cc: caml-list

>But that dead code if executed eagerly will result
>in a value stored in a variable which is never used.
>So the code is still 'dead code' and still a bug
>(assuming it is a bug) whether evaluation is eager
>or lazy.

This is where dead code elimination comes into play. A trivial optimization 
performed by the GHC compiler.

e.g.

let x = <long computation>
    y = ...
in  ...y....

we can remove 'x'.

Again, side-effects make this harder in o'caml.

>Actually the lazy case might make it easier to
>track this down by adding a debugging print
>in the expression -- in the eager case you get
>a diagnostic but can't deduce the result is used,
>in the lazy case you know the result is being used
>when you get the diagnostic.

The compiler should optimize it out. There shouldn't be any need for using 
explicit print statements.

Jason.

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 23+ messages in thread
* RE: [Caml-list] laziness
@ 2004-09-06  9:16 Jason Smith
  0 siblings, 0 replies; 23+ messages in thread
From: Jason Smith @ 2004-09-06  9:16 UTC (permalink / raw)
  To: Jason Smith; +Cc: caml-list

>> I'd have thought that you cannot determine evaluation order in a purely
>> functional language and, therefore, you couldn't distinguish between eager
>> and lazy evaluation in this case.
>
>That isn't so if you can have functions that fail or fail to terminate.
>In those case laziness matters.
>Clearly that's more cases than
>you'd guess, otherwise Haskell would be a pointless language:

A function in haskell 'that fails to terminate' will either recurse ad 
infinitum. In which case thats the programmers fault, if Haskell evaluates it 
and it doesn't stop, ur problem.

Or, it will recurse over a Sum type. Haskell like most mainstream functional 
languages does not support recursive types, therefore we have to structure it 
explicitly over the Sum data type. With this you can create 'infinite' data 
structures, e.g.

fib = 1 : 1 : [ a + b | (a, b) <- zip fib (tail fib)]

we can keep on accessing this list by popping of the head element "forever".

>I'm not a Haskell programmer but I imagine the laziness is actually
>exploited (just as Ocaml programmers exploit HOF's and closures
>so routinely we forget we're doing it until some C++ programmers
>asks if we actually use such advanced features .. :)

Laziness is a *very* usefull feature, you can save *huge* amounts of unneeded 
calcluations. Any recursive data structure be it binarytree's, octtree's, 
lists etc.. benefits from this, you only evaluate expressions in the structure 
*when u need them* i.e. when u need there WHNF values, say in a condition 
statement.

Jason.


p.s. John skaller, I was wondering why I keep getting undeliverable errors 
when I send things to your address?

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 23+ messages in thread
* RE: [Caml-list] laziness
@ 2004-09-06 12:17 Jason Smith
  2004-09-06 17:00 ` skaller
  0 siblings, 1 reply; 23+ messages in thread
From: Jason Smith @ 2004-09-06 12:17 UTC (permalink / raw)
  To: Jason Smith, skaller; +Cc: caml-list, Richard Jones

>===== Original Message From skaller@users.sourceforge.net =====
>On Mon, 2004-09-06 at 19:07, Jason Smith wrote:
>
>> The compiler should optimize it out. There shouldn't be any need for using
>> explicit print statements.
>
>Yes but the original issue is that the programmer
>is seeing an expression they expect to be evaluated
>and it isn't being evaluated -- so there is a bug
>somewhere.

I'm not sure i follow you here. How can you *not* use something? if you want 
to use an expression you "use" it, I'm sorry, I'm missing something obviously.

>So your point is kind of backwards --
>the compiler may well optimise it away, but the programmer
>is actually looking for evidence that it *isn't*
>optimised away.

Um, ok so ur depositing debug statements in code to check if the expression is 
still there.  I still fail to see why u can't just *look* at your code and see 
if ur using the value or not. Can I get an example?

>In an eager language, no conclusions can be drawn
>from a debugging output - you still don't know
>if the returned value is used or not.

True, well almost true. The compiler can determine even in a strict language 
if the value is used or not, using strictness analysis. So you could put 
debugging statements in an expression and expect to see them and then volla, 
do not. For example,

val f x y => x + 2;

> f 2 (some computation that yields a number)

4

the long compuation was never actually used within 'f' so we don't have to 
evaluate it eagerly in haskell. Unforunately as I have already pointed out in 
a previous post in O'Caml it'd be harder to make this decision, but not 
impossible.

>In a lazy language, debugging output indicates
>the code *is* being used and hence not dead,

Yup.

>and lack of output means it isn't, at least
>in one particular environment.

Yup.

>You might investigate further and discover the result
>is only used in an unreachable branch of a match,
>so the code really is dead: that function argument
>will never be used (so you can remove it,
>and also the unreachable branch).

Ah nope, if its 'unreachable' then the original expression will never be 
evaluated, and you won't get the debug statements, and more so GHC won't 
compile it to start with. As soon as you use the expression, u reduce it. The 
*result* of evaluating the scrutinee expresion for a case (aka conditional 
expression) must be used because it determines the branch of the conditional 
to take. Weather that branch actually uses the result is irrelevant at that 
point.

Cheers, 
Jason.

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners


^ permalink raw reply	[flat|nested] 23+ messages in thread

end of thread, other threads:[~2004-09-07  8:38 UTC | newest]

Thread overview: 23+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-09-04  6:30 [Caml-list] laziness skaller
2004-09-04  8:40 ` Marcin 'Qrczak' Kowalczyk
2004-09-04 11:21   ` skaller
2004-09-04 11:49     ` Richard Jones
2004-09-04 20:40     ` Hartmann Schaffer
2004-09-05 10:50 ` Jon Harrop
2004-09-05 14:07   ` skaller
2004-09-05  1:07 Jason Smith
2004-09-05  5:46 ` skaller
2004-09-06  0:57 ` Richard Jones
2004-09-06  6:11   ` Nicolas Cannasse
2004-09-06  8:24   ` skaller
2004-09-06  8:44   ` Erik de Castro Lopo
2004-09-06 12:55   ` Jon Harrop
2004-09-06 16:21     ` William Lovas
2004-09-06 22:35   ` Hartmann Schaffer
2004-09-07  8:31     ` Richard Jones
2004-09-07  8:37       ` Nicolas Cannasse
2004-09-06  9:07 Jason Smith
2004-09-06 10:18 ` skaller
2004-09-06  9:16 Jason Smith
2004-09-06 12:17 Jason Smith
2004-09-06 17:00 ` skaller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).