Open spcfox opened 4 days ago
Have you tried with the --directive lazy=weakMemo
compilation directive? (documented here) PR #2791 added weak memoization of lazy values, but it looks like it's guarded by that compilation directive because it caused performance regression in some cases.
Yes, memoization is a good way to combat this problem (although given that memoization is weak, I'm not sure the problem wouldn't show up in more complex examples). But I think it's an undesirable behavior, and it would be nice to fix it. I think the filter
example is pretty illustrative.
Gist with code
Steps to Reproduce
After pattern-matching on lazy values, and then using the binded name, they are computed again.
g
computes the lazy value only once, andh
computes the lazy value twice. But in practice we usually write as inf
, which behaves likeh
, which leads to performance issue.A similar problem occurs when using
case
:Problem with
filter
This becomes a major problem in polymorphic functions when we have no way to explicitly force the evaluation.
An example of such a function is
filter
. It behaves similarly to the functions above: it evaluates a value, and in a result uses the name binded with the original argument. Therefore, it is inefficient to filter lazy values. Instead offilter (p . force)
it is more efficient to domap delay . filter p . map force
. Here are measurements demonstrating this problem:Lines with
filter
compute the lazy valueiterations
times, while lines withfilterLazy
only 1 time:I think it's currently impossible to write a polymorphic
filter
that handles lazy values efficiently.Expected Behavior
f
should be equivalent tog
(maybeh
too?).filter (p . force)
should behave likemap delay . filter p . map force
Observed Behavior
g
is more effective thanf
map delay . filter p . map force
is more effective thanfilter (p . force)