List.reduce(), List.reduced(), and reduce() all return a List now.
Previously, reduce() returned the only item in the reduced list. Not
to long ago, List.reduce returned nothing, and List.reduced() returned
a List or the only item in the list; I'm no longer certain.
reduce([1,2,3], add) returns [6]. If you want a classic reduced
value, you'll have to pop it off. Of course, "reduce" usually takes
its binary relation function as its first argument, but I prefer to be
consistent about making the global function variant of a method accept
what would be the context object as the first argument and functions
as the last argument since that's so much easier to make look good in
JavaScript what with continuation passing style.
List.reduce() does an in-place reduction, so the existing list is
modified. This operation "chains"; that is, it returns itself so you
can perform more stateful or stateless operations on the list, like
"pop" perhaps. This is just like "reversed" or "sorted".
So, to be consistent, I made List.reduced(), the stateless variant,
return the new list instead of the single value in it. This means you
can easily change your mind between the stateless and stateful
variants, since they have the same type profile, as you would expect
from any stateful/stateless pair of functions.
Again, let me know if you're offended by this API choice or if I broke
your toys by it.
Kris
Also, what about currying-in the binary relation on the global variant
of reduce? Would that not be a useful pattern?
--Ryan
On May 31, 2008, at 1:32 AM, "Kris Kowal" <kris....@cixar.com> wrote:
>
> Running the litmus test (an aggregation of bellwether unit tests)
> again revealed that I had broken a couple of my old assertions in an
> unadvertised API change. From now on, when I have to change an
> assertion in the unit tests, I'm going to email the list. I modified
> the List.reduce function a while back to be consistent with other
> functions with -ed variants. This reduces the code complexity and
> thus the learnability (I think) but might result in a case you might
> not expect coming from other languages (wait...who uses reduce?). Let
So, no, chaining isn't useful for reduce operations...unless you chain to push.
l = range(10).reduce(add).add(range(10, 20)).reduce(add)
Or, alternately stateless:
l = range(10).reduced(add).added(range(10, 20)).reduced(add)
The central point of the decision to move this way was to make the API
more orthogonal (I'm sure I made this choice in response to a code
tweak that I expected to work, but didn't) at the price of confusing
people who already know and love reduce.
What do you guys think? I can change these functions back, such that:
List.reduce would be chainable, would return the modified, single-item list
Iterable.reduced would not be chainable, would return the only
remaining item of a reduced list
reduce would be polymorphic on .reduced, so would also not be
chainable and would return the only remaining element of a reduce
iterable.
So, one thing this comes out of is an emergent pattern in functions
that end with -ed. They're pretty easy to construct from their
respective stateful method. So this is generally how it looks:
this.reverse = function () ...
this.reversed = ed(reverse);
So, at the moment, reduced is defined like:
this.reduced = ed(reduce);
But that can be changed pretty easily to return the only element of
the resultant list:
this.reduced = ed(reduce).to(get(0));
;-)
Kris