I have a simulation with lots of calls to functions of the type F = A -> B -> C -> D
, where A
..D
are concrete types.
The objects of type A
have a medium lifetime. (It is the genome of codegolf's ratrace.)
The most expensive computation arises from parameter A
. I can easily memoize like this:
f1 :: F
f1 a = let expensive = trace "expensive computation!" $ expensiveComputation a
in \b c -> expensive
and hold some pre-processed expensive
values via partial application:
preProz :: [B -> C -> D]
preProz = [f1 [], f1 [False], f2 []]
The traces indicate that preProz <*> [[],[[]]] <*> [1,2]
does not recompute the values to my delight.
Now I found out that some of my F
s would benefit from pre-processing B
, too. This pre-processing is independent from A
, and, in fact, memoizing like this has no benefit
f2 a = let expensive = trace "expensive computation!" $ expensiveComputation a
in \b -> let dear = trace "expensive computation!" $ expensiveComputation b
in expensive + dear
because dear
is recomputed, even is the b
s equal.
What I need is something like:
(B -> e) -> A -> e -> C -> D
where e
should be memoized. The type of e
is sort-of-existential here.
But this forces me to recompute all values A
for every B
, which is just as bad, and I cannot save the e
s, which are private to the function.
How can I memoize along 2 parameters independently?
You need a function that memoizes both a
and b
together:
f12 a b = ...
in \c -> ...
When you want to memoize a
but not b
, you use f1 a
and when you want to memoize both you use f12 a b
.
It would of course be nice to share some implementation between f1
and f12
. However, you can do that only by having private functions that take the precomputed results in place of the original values:
f1 a = privateA (precomputeA a)
f12 a b = privateAB (precomputeA a) (precomputeB b)
privateA a' b = privateAB a' (precomputeB b)
private AB a' b' c = ...
If the precomputation of b
depends on the precomputation of a
, then:
f1 a = privateA (precomputeA a)
f12 a b = let a' = precomputeA a in privateAB a' (precomputeB a' b)
privateA a' b = privateAB a' (precomputeB a' b)
private AB a' b' c = ...
I've purposely not used function composition and eta-reduction, to make things clearer. I've also left out any strictness annotations that you might want to use to control times of evaluation.
Perhaps memoizing isn't quite the right term here. You mean something like "partial application with some precomputation as well."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With