Following up on Functional-Style Fixed-Length Queue Object?, I want to generalize it to a functional-style accumulator pattern. Lots of things look like accumulators, loosely defined as binary functions that take a state-carrying object as a first parameter, a datum as a second parameter, and return the state-carrying object. Such an accumulator can be scanned, mapped, or (theoretically, pending answer to the cited question) folded over sequences or streams of data to yield interesting results. Some things that look like accumulators are statistics, filters, smoothers, estimators, hidden Markov models (Kalman, Viterbi), controllers, I am sure many more.
For elegance, I'm interested in a functional style. To keep the example small, consider the following:
newAccumulator[] := <|"runningSum" -> 0|>;
SetAttributes[accumulate, HoldFirst];
accumulate[accumulator_, datum_?NumberQ] :=
(accumulator[["runningSum"]] += datum;
accumulator);
This will just keep a running sum, but we could easily keep higher-order statistics and such a thing can be quite rich and useful.
We can use this easily to compute Gauss's Day-of-Reckoning Sum, for example;
$myAccumulator = newAccumulator[];
Scan[accumulate[$myAccumulator, #] &, Range[100]];
$myAccumulator
<|"runningSum" -> 5050|>
Now, it occurred to me, "wouldn't it be nice if we could write
Scan[partial[accumulate, $myAccumulator], Range[100]]
where
SetAttributes[partial, HoldRest];
partial[f_, arg_] := (f[arg_,#]&);
which I can unit-test as follows
partial[f, x][y]
f[x, y]
and even generalize (just a little):
partial[f_, as__] := (f[Sequence @@ (Quiet @ Join[as, ##])] &);
and unit-test
partial[f, x, y][z, w]
partial[f, x][y]
partial[f, x][]
f[x, y, z, w] f[x, y] f[x]
But, lo and behold, it doesn't work on my example:
$myAccumulator = newAccumulator[];
Scan[partial[accumulate, $myAccumulator], Range[100]]
$myAccumulator
<|"runningSum" -> 0|>
This one has me clueless.
EDIT: The HoldRest
attribute on partial
does not seem to make a difference. I put it in there out of habit -- side-effecting functions often work best when they hold their arguments against premature evaluation.