3 ms·
> Relying on lazy semantics also wrecks parallelism, because some evaluation branches cannot be visited ahead of time, without changing the program's semantics.
by triplepoint217 13y ago
> Relying on lazy semantics also wrecks parallelism, because some evaluation branches cannot be visited ahead of time, without changing the program's semantics.
For a pure functional language, like Haskell, speculatively evaluating pieces of it in parallel shouldn't hurt anything (since they are guaranteed not to have side effects).
It is my understanding that the lazy semantics of Haskell mostly mean that you shouldn't be making any assumptions about the order of evaluation which would seem to be useful from an optimization perspective.
I am still new to FP and Haskell, so please correct me if I am misinterpreting things.
- yvdriess 13y ago> It is my understanding that the lazy semantics of Haskell mostly mean that you shouldn't be making any assumptions about the order of evaluation which would seem to be useful from an optimization perspective. A crudely example, to impart some intuition: True || this_crashes(); Imagine the above reliance on lazy semantics, but then for Haskell memory management or constructs such as infinite lists. The semantics of your program rely on the fact that some branches of your AST/program are not visited, due to lazy semantics. A FP with eager evaluation semantics can be free to evaluate every branch of the program, throwing away what isn't used. Lazy evaluation means a runtime cannot be as aggressive, introducing a number of logical sequence points. NB. A Haskell colleague lets me know that it is considered good style in Haskell these days to program as if it was eagerly evaluated. There is talk of limiting or doing away with it in Haskell Prime. Can anyone confirm/deny this?
- zwegner 13y ago> True || this_crashes(); That's not functional though--this_crashes() has side effects. I'm far from a Haskell expert, but I guess in practice you'd be returning some error type from that function, and it didn't matter whether it's evaluated or not, since True || arbitrary_expression will always just be True. Laziness, AFAIK, only complicates compilation due to making it hard to reason about memory/performance, and for having branches all over to see if expressions have been evaluated yet.
- Tuna-Fish 13y agoConsider > True || construct_a_1TB_object() Unless you have infinite computation and memory, there are always visible side-effects, no matter how functional your language is.
- dllthomas 13y agoIf the 1TB object is constructed atomically, it has to wait, yes - but really, that's the case in eager languages too if you want the same final semantics. If the 1TB object can in any way be constructed a bit at a time, then in principle you can get some work done in constructing it and back that out if you wind up not needing it.
- triplepoint217 13y agoFair enough. Though as later comments point out, constructing a 1tb object is probably a better failure case for functional languages. It would also be fairly easy to avoid if the True on the lhs could be evaluated quickly so you could abort the second calculation. That kind of code is only valid in strict languages because of short circuiting, is functionally equivalent to lazyness in this respect. If you tried to automatically parallelize C by speculatively evaluating both sides you would have exactly the same problem. Lazy languages may make this kind of code easier to write, but the problem is actually that of non-deterministic calculations, and would be a problem anywhere.
- driax 13y agoCrashing would be a side-effect. However your idea is correct. Consider instead: [True, SpinForever()] You can consider any "variable" in Haskell to have a value of the type of that variable or the bottom value. The bottom value is tech-speak for no-value at all, since it means that the program has gone into a infinite-loop trying to produce the value. Eagerness would fix this since, such a program as above would always cause a infinite-loop, and thus the program would no longer be a useful construction. While with lazy evaluation it only spins if you try to evaluate any more than the first element in the list. Of course you could also fix this by going with total programming which basically removes the implicit bottom-value from the language. Which is a nice concept. Though then you need to explicitly declare you infinite data from you finite data (think a network socket, compared with string). And you lose Turing-completeness (but who needs that anyway :) )