15.4.4.21 Array.prototype.reduce ( callbackfn [ , initialValue [ , thisArg ] ] )

2009-03-21 Thread Edward Lee
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 ] ] )

2009-03-21 Thread David-Sarah Hopwood
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 ] ] )

2009-03-21 Thread Brendan Eich

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 ] ] )

2009-03-21 Thread Douglas Crockford

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 ] ] )

2009-03-21 Thread David-Sarah Hopwood
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 ] ] )

2009-03-21 Thread Edward Lee
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-03-21 Thread Igor Bukanov
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 ] ] )

2009-03-21 Thread Brendan Eich

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