
Jedaï wrote:
[...] Yes, but in the second version, it has to reconstruct (S a) before comparing it to "one" where in the first it could do the comparison directly. In this cas there may be some optimisation involved that negate this difference but in many case it can do a real performance difference. The "as-pattern" (@ means as) is both practical and performant in most cases.
Chris wrote:
The first example takes apart the (Just n) and later reconstructs (Just n). Unless the compiler is fairly clever, this will cause the new (Just n) to be a new allocation instead of reusing the input value. The second form uses an
I tend to believe that the '@' notation is mere syntactic sugar. Indeed, it seems to me that Haskell compilers need not to be very clever to share the identical sub-expressions, for one very simple reason implied by Haskell semantics: referential transparency. Am I right, or am I missing something? And, of course, I'm not saying that syntactic sugar is bad. As Alfonso noted, it saves some typing and avoids screwing up while trying to modify multiple occurrences of the same sub-expression. And, an even more important I think, a software engineering benefit: It emphasizes for the reader that we really are talking about the very same thing at two (or more) different places. --Serge No virus found in this outgoing message. Checked by AVG Free Edition. Version: 7.5.516 / Virus Database: 269.17.9/1198 - Release Date: 26/12/2007 17:26

Serge LE HUITOUZE wrote:
I tend to believe that the '@' notation is mere syntactic sugar. Indeed, it seems to me that Haskell compilers need not to be very clever to share the identical sub-expressions, for one very simple reason implied by Haskell semantics: referential transparency.
Am I right, or am I missing something?
You are missing that modifying a program in a way which adds sharing can have disastrous performance effects. In particular, adding sharing can stop something being GCed, which can convert an algorithm which runs in linear time and constant space to one which runs in linear space (and therefore, perhaps, quadratic time). Therefore compilers have to be conservative about adding sharing. In general, sharing in GHC-compiled programs is specified rather explicitly: sharing is naming, so things which are names (let-bound or pattern-bound) are guaranteed shared and normally nothing else. You can *imagine* a compiler being cleverer about this. It turns out to be an interesting but hard problem. Jules

On Dec 28, 2007 9:35 AM, Jules Bean
In particular, adding sharing can stop something being GCed, which can convert an algorithm which runs in linear time and constant space to one which runs in linear space (and therefore, perhaps, quadratic time).
I've heard of this before, but can you give an example? Luke

Luke Palmer wrote:
On Dec 28, 2007 9:35 AM, Jules Bean
wrote: In particular, adding sharing can stop something being GCed, which can convert an algorithm which runs in linear time and constant space to one which runs in linear space (and therefore, perhaps, quadratic time).
I've heard of this before, but can you give an example?
Sure: foo (length (build_big_list 1 2 3)) (build_big_list 1 2 3) Here, "foo" is a function which operates on a list, but needs to know the length of the list for some reason. As written, build_big_list is called twice. This is potentially a waste of time, but it *does* mean that if the big list is very very big (larger than available memory) but build_big_list is a good producer, it can be produced and GCed in constant space - as long as length is a good consumer (which it is) and as long as foo is (can't tell without seeing the definition, obviously!) So, you can do a two-pass algorithm in two-passes, but in constant space. Now, if we automatically share those two, we get something like this: let l = build_big_list 1 2 3 in foo (length l) l which *looks* like an optimisation, but the shared 'l' will be kept around. The length will be calculated first (assuming foo uses the length, that is) and then the entire list kept in memory while foo processes it. Of course, it's hard to see this kind of problem with the simple x@(Just y) case we were talking about, but if your function calls itself recursively then it can. Jules

On Fri, 2007-12-28 at 09:51 -0700, Luke Palmer wrote:
On Dec 28, 2007 9:35 AM, Jules Bean
wrote: In particular, adding sharing can stop something being GCed, which can convert an algorithm which runs in linear time and constant space to one which runs in linear space (and therefore, perhaps, quadratic time).
I've heard of this before, but can you give an example?
I don't know why they were so modest. Run the following two programs in the Haskell implementation of your choice 1) powerset [] = [[]] powerset (x:xs) = powerset xs ++ map (x:) (powerset xs) 2) powerset [] = [[]] powerset (x:xs) = xss ++ map (x:) xss where xss = powerset xs Compare it on programs such as length (powerset [1..n]) for n in the range of 20-30. Make sure you aren't doing anything important when you use the second version for larger inputs. These two programs are an extreme case of trading space for time, and an extreme case of why common subexpression elimination can be a massive pessimization. In this particular case, there is an exponential increase in the size of the live-set.
participants (4)
-
Derek Elkins
-
Jules Bean
-
Luke Palmer
-
Serge LE HUITOUZE