15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
Right now [].reduce doesn't take an optional thisArg, so the callback is always called with |null| for |this| per 9.c.ii. The Array prototype object take an optional thisArg for every, some, forEach, map, and filter; so it would be good to make reduce consistent with the rest. The new parameter list for Array.prototype.reduce would then be.. 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] ) Insert after step 4: #. If thisArg was supplied, let T be thisArg; else let T be undefined. For step 9.c.ii, replace: callbackfn with null as the this value with.. callbackfn with T as the this value Similarly, this should be applied to reduceRight as well: 15.4.4.22 Array.prototype.reduceRight ( callbackfn [ , initialValue [ , thisArg ] ] ) The only remaining Array callback is sort, which could be updated as well: 15.4.4.11 Array.prototype.sort ( comparefn [, thisArg ] ) Ed ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
Edward Lee wrote: Right now [].reduce doesn't take an optional thisArg, so the callback is always called with |null| for |this| per 9.c.ii. The Array prototype object take an optional thisArg for every, some, forEach, map, and filter; so it would be good to make reduce consistent with the rest. Why is it better to use 'this' than to simply have the callback function capture the variables it needs? The latter is just as expressive and IMHO results in clearer code, since: - the captured variables are named, and the names can be more meaningful than 'this'; - there can be more than one such variable, without needing to set 'this' to an object or list. The required variables are necessarily in scope when passing a FunctionExpression as the callback. The case where they are not in scope because the callback function is defined elsewhere is quite unusual; in that case, you can instead pass a lambda expression that calls the function defined elsewhere with these variables as explicit parameters. (That is a situation where using 'this' results in particularly *unclear* code, because the definition of what 'this' is set to may be far away from its use.) The other methods with callbacks take a 'thisArg' not because it is needed or even useful, but for compatibility, because they already do in existing implementations that provide these functions. -- David-Sarah Hopwood ⚥ ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
On Mar 21, 2009, at 9:01 AM, Allen Wirfs-Brock wrote: Adding to David-Sarah's comments, here is how I would expect a call that needs to supply a thisArg to be expressed: a.reduce(f.bind(thisArg),init) That's one way; the other way was to use a closure. Dave Herman developed the reduce (and reduceRight) specs for ES4 and JS1.8 based on my proposals, without thisArg in order to keep the signature short and Pythonic. Here's Dave's write-up, I don't think he'll mind me sharing it: SYNOPSIS: - Haskell and ML both have essentially the same versions of foldl (i.e., reduce left-to-right) and foldr (i.e., reduce right- to-left), other than laziness and slight differences in currying. - Haskell also has foldl1 and foldr1, which don't take the basis element argument, and use the first element of the list instead. - SRFI 1 has fold and fold-right, which are similar to ML and Haskell, except they can take any number of lists, and the function argument has to accept one argument for every list. (This is hard to give precise types for, though, and it doesn't fit into a single- dispatch OO paradigm.) - SRFI 1 also has what it calls reduce and reduce-right, which are optimized special cases of fold and fold-right: they require the basis element to be a right identity of the function argument, so they don't have to call the function argument in the one-element case (in case that function is expensive). This is only applicable when the basis element is a right identity, though. - Python has reduce which takes a function, a list, and optionally a basis element. If the basis element is provided, it's essentially foldl. If the basis element is not provided, it's essentially foldl1. MY SUGGESTION: I think we should offer a reduce method similar to Python's except as a method on arrays (to be consistent with JS 1.6's map). However, because of the JS 1.6 precedent for the signature of map, it's gonna be hard to add an optional argument, since it already accepts an optional thisObject. I'm guessing this is because we won't have bound methods until ES4, so anything that accepts a callback should also accept an optional this object to use in the call to the callback. So this would mean reduce takes one callback argument and two optional arguments: thisObject and init. Which one should come first? The more common one is probably init, but then you separate the callback arg from the thisObject arg, which is maybe okay. Multiple optional arguments are kinda messy this way... Alternatively, we could just eliminate that extra thisObject argument, since people can always do something like: arr.map(function(x,i,me) { return obj.method(x,i.me) }) to ensure that the desired method gets called with the appropriate binding for this. Then in ES4 they'll be able to do even better with bound methods. This way we could then make the signatures of map and reduce signature really simple: map(callback) reduce(callback[, init]) The benefits: - just like Python = Python community mindshare - full generality of fold (left) - but also make the simple case, where the first element is the basis element, simpler I'd guess most people find the left-to-right version of reduce more intuitive, since they usually iterate over arrays from left to right. Plus that's what Python does. I think it would also be important to provide a reduceRight as well, since not every operation is associative, and sometimes people need to go from right to left. Details attached. Dave 1.) Haskell [http://www.haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html#3 ] foldl :: (a - b - a) - a - [b] - a foldl f z [x1, x2, ..., xn] = f (f ... (f (f z x1) x2) ...) xn foldr :: (a - b - b) - b - [a] - b foldr f z [x1, x2, ..., xn] = f x1 (f x2 ... (f xn z) ...) foldl1 :: (a - a - a) - [a] - a foldl1 f (x1:xs) = foldl f x1 xs foldr1 :: (a - a - a) - [a] - a foldr1 f (x1:xs) = foldr f x1 xs 2.) ML [http://www.standardml.org/Basis/list.html#SIG:LIST.foldl:VAL] foldl : ('a * 'b - 'b) - 'b - 'a list - 'b foldl f init [x1, x2, ..., xn] = f(xn, ..., f(x2, f(x1, init)) ...) foldr : ('a * 'b - 'b) - 'b - 'a list - 'b foldr f init [x1, x2, ..., xn] = f(x1, f(x2, ..., f(xn, init) ...)) 3.) SRFI 1 [http://srfi.schemers.org/srfi-1/srfi-1.html#FoldUnfoldMap] fold : ('a1 * ... * 'ak * 'b - 'b) * 'b * 'a1 list * ... * 'ak list - 'b (fold f init (list x1 x2 ... xn)) = (f x1.n x2.n ... xk.n ... (f x1.2 x2.2 ... xk.2 (f x1.1 x2.1 ... xk. 1 init)) ...) fold-right : ('a1 * ... * 'ak * 'b - 'b) * 'b * 'a1 list * ... * 'ak list - 'b (fold-right f init (list x1 x2 ... xn)) = (f x1.1 x2.1 ... xk.1 (f x1.2 x2.2 ... xk.2 ... (f x1.n x2.n ... xk.n init) ...)) reduce : ('a * 'a - 'a) * 'a * 'a list - 'a PRECONDITION: (f x ridentity) = x (reduce f ridentity ()) = ridentity (reduce f ridentity (list x1 x2 ... xn)) = (fold f x1 (list x2 ... xn))
Re: Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
Brendan Eich wrote: On Mar 21, 2009, at 9:01 AM, Allen Wirfs-Brock wrote: By breaking symmetry we have made at least one browser-based implementation precedent (Rhino also supports reduce and reduceRight) with no thisArg for the fold methods. At this point I would rather we stand on precedent than waver or haver further. I agree with Brendan on this. ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
Edward Lee wrote: On Sat, Mar 21, 2009 at 9:50 AM, David-Sarah Hopwood david.hopw...@industrial-designers.co.uk wrote: Why is it better to use 'this' than to simply have the callback function capture the variables it needs? It's nice to be able to consistently refer to the same 'this' from an prototype's function whether from inside a local lambda or another function on that prototype. Any generic function that takes 2 arguments and returns 1 can be used for reduce, but if that callback if a prototype's function, its 'this' will be wrong unless you provided extra code to bind the function to an object. Yes, you can achieve this in other ways by just binding the callback to the object before passing it to reduce, so one minor benefit is that it's more compact: [].reduce(fn, init, this) [].reduce(fn.bind(this), init) Very minor. '.bind(this)' has the advantage of working in general for all such cases, not just for particular Array methods. In the thisless style where objects are constructed as closures rather than using prototypes, of course, this problem never happens. But the main reason is just consistency with the rest of the functions that take a callback. I accept that consistency is a valid consideration; I just don't think it outweighs the considerations given in my previous post. I'm not strongly opposed to adding 'thisArg' to these functions, though, if the concensus is in favour. My argument is primarily that they're not needed and that it is better for programs to use variable capture, and either the thisless style or '.bind(this)', instead. -- David-Sarah Hopwood ⚥ ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
On Sat, Mar 21, 2009 at 11:57 AM, Brendan Eich bren...@mozilla.com wrote: Of course, all the other array extras are specified with a thisArg so consistency argues that reduce and reduceRight should be specified that way too. I had to remind myself of Dave's analysis/argument, cited in full above, to see why we didn't do that. What exactly was Dave's argument for not including a thisArg? There's a brief mention of multiple-optional-args-messiness, and then it talks about a future where other array extras don't have a thisArg. So this would mean reduce takes one callback argument and two optional arguments: thisObject and init. Alternatively, we could just eliminate that extra thisObject argument, arr.map(function(x,i,me) { return obj.method(x,i.me) }) vs arr.map(method.bind(obj) vs arr.map(method, obj) At this point I would rather we stand on precedent than waver or haver further. Reasonable. (I'll just probably end up using bind for all the other array extras ;)) Ed ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
2009/3/21 Edward Lee edi...@mozilla.com: On Sat, Mar 21, 2009 at 11:57 AM, Brendan Eich bren...@mozilla.com wrote: ... Alternatively, we could just eliminate that extra thisObject argument, arr.map(function(x,i,me) { return obj.method(x,i.me) }) vs arr.map(method.bind(obj) vs arr.map(method, obj) The last line is no exact alternative to the previous one. In full one would write: arr.map(obj.method.bind(obj)) or arr.map(obj.method, obj) IMO this double-usage of obj looks strange compared with the closure version even if the latter is too verbose. Igor ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: 15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )
On Mar 21, 2009, at 10:26 AM, Edward Lee wrote: On Sat, Mar 21, 2009 at 11:57 AM, Brendan Eich bren...@mozilla.com wrote: Of course, all the other array extras are specified with a thisArg so consistency argues that reduce and reduceRight should be specified that way too. I had to remind myself of Dave's analysis/argument, cited in full above, to see why we didn't do that. What exactly was Dave's argument for not including a thisArg? There's a brief mention of multiple-optional-args-messiness, and then it talks about a future where other array extras don't have a thisArg. Dave wrote: So this would mean reduce takes one callback argument and two optional arguments: thisObject and init. Which one should come first? The more common one is probably init, but then you separate the callback arg from the thisObject arg, which is maybe okay. Multiple optional arguments are kinda messy this way... Indeed the other Array extras take (callback [, thisArg]) where [] means optional, but reduce and reduceRight want an optional initialValue fold basis. Without named parameters there's a problem, since the initialValue is probably more commonly supplied than thisArg (we intuit, and I think this is true from the reduce uses I've seen, but more data welcome). We could surely give reduce the parameters (callback [, initialValue [, thisArg]]) but as Dave noted this breaks one symmetry (a more superficial kind than the loss of thisArg? nevertheless...) with the other Array extras, by not putting the optional thisArg parameter immediately after callback. The other way 'round makes the initialValue parameter case have to supply a dummy thisArg, according to the hypothesis that initialValue is more commonly supplied in actual use cases. Then there's the Pythonic argument: just like Python = Python community mindshare. Of course Python's reduce is a function, not a method, but the mapping is straightforward. /be ___ Es-discuss mailing list Es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss