From: D. Tweed [mailto:tweed@compsci.bristol.ac.uk]
Note that (assuming that I'm not missing something) you can prevent the moving of expressions involving l in a very ugly way by noting that these `dummy argument functions' are polymorphic so that you could write
x1 = f1 (l 1) x2 = f2 x1 (l 2) x3 = f3 x2 (l 3)
Please don't encourage this sort of thing. If two expressions are semantically equivalent, there's always a chance that the compiler will replace one by the other or common them up. In this case, I believe GHC's worker-wrapper transformation will do it. Cheers, Simon
On Wednesday 18 September 2002 11:21 am, Simon Marlow wrote:
From: D. Tweed [mailto:tweed@compsci.bristol.ac.uk]
Note that (assuming that I'm not missing something) you can prevent the moving of expressions involving l in a very ugly way by noting that these `dummy argument functions' are polymorphic so that you could write
x1 = f1 (l 1) x2 = f2 x1 (l 2) x3 = f3 x2 (l 3)
Please don't encourage this sort of thing. If two expressions are semantically equivalent, there's always a chance that the compiler will replace one by the other or common them up.
In this case, I believe GHC's worker-wrapper transformation will do it.
I still think it's a real problem though. With Haskell as it is at the moment there seems to be no alternative to these ugly (and unreliable) hacks. Clean allows programmers to distinguish between constants and "functions" with zero arguments. Couldn't Haskell be modified to do the same? e.g. Instead of writing..
squares = [i*i| i <- [1..]]
..we explicitly specify a function with no arguments (implying no sharing allowed).
squares = \ -> [i*i| i <- [1..]]
I'm not sure what the semantic consequences of this would be, or even if the concept of 'sharing' has any sensible meaning in Haskell. (There's probably an implicit assumption here that all Haskell implementations will be based on some kind of graph reduction, which isn't necessarily so.) Regards -- Adrian Hey
I still think it's a real problem though. With Haskell as it is at the moment there seems to be no alternative to these ugly (and unreliable) hacks. Clean allows programmers to distinguish between constants and "functions" with zero arguments. Couldn't Haskell be modified to do the same?
I completely agree that it is a problem for real world programs. As for the solution, how to hint to the compiler that a certain value should be evaluated as soon as possible, other value as needed, and still other should be reevaluated everytime it is used, there are many possibilities. This includes Strategies, strictness annotations, compiler pragmas. Those hints should not affect the result of the computation, only the way the result is computed, therefore they do not necessarily need to be part of the language but it would certainly be easier to use if they were. Maybe in some years when the compilers get very very clever, we will not need to give those hints and an optimal code will be produced anyway. But we are not yet there. My limited experience comes from implementing a large numerically oriented program with complicated data structures in Haskell. I found that the first prototype can be developed quickly and the language is very expressive and safe at the safe time, which is a rare quality. On the other hand, the resulting code in inefficient and scales very poorly. While the first prototype could be used to handle problems of small sizes, it quickly used up all available memory when applied to bigger problems. A strictness annotation was essential to get it run at more acceptable speed and to use less memory. Another set of annotations would be very helpfull in indicating what we want to recalculate because we cannot afford to cache it. I solved the problem using some hacks in a very crude way but I am still looking for a better solution. (By the way, this this could be done automatically - when the real time system runs out of memory, it could simply delete some cached values, as they can almost always be recalculated.) Cheers, Jan -- ------------------------------------------------------------------------- Jan Kybic <kybic@ieee.org> Odyssee, INRIA, Sophia-Antipolis, France or <Jan.Kybic@sophia.inria.fr>,tel. work +33 492 38 7589, fax 7845 http://www-sop.inria.fr/odyssee/team/Jan.Kybic/index.en.html
(By the way, this this could be done automatically - when the real time system runs out of memory, it could simply delete some cached values, as they can almost always be recalculated.)
People keep toying with this idea but no-one ever seems to implement it. One of the problems is that heap sizes can go up as well as down. A thunk like: length [1,2,3,4,5,6,7,8,9,10] :: Int or 1+1+1+1+1+1+1+1+1+1+1+1+1+1 :: Int will get smaller when fully evaluated. A thunk like: enumFromTo 1 10 :: [Int] will get larger when fully evaluated. Another problem is that if you want to revert an object to its original form you have to keep the unevaluated thunk. That thunk may be larger than the evaluated object. Even if it is smaller, it is still an overhead that we would normally strive to avoid. Finally, it is hard to determine the size of a thunk because it often shares some of its structure with other thunks. -- Alastair Reid alastair@reid-consulting-uk.ltd.uk Reid Consulting (UK) Limited http://www.reid-consulting-uk.ltd.uk/alastair/
participants (4)
-
Adrian Hey -
Alastair Reid -
Jan Kybic -
Simon Marlow