Argument unpacking?

classic Classic list List threaded Threaded
17 messages Options
Reply | Threaded
Open this post in threaded view
|

Argument unpacking?

Erik Arvidsson
Is argument unpacking like in Python planned?

See http://docs.python.org/tut/node6.html#SECTION006740000000000000000
for an introduction.

--
erik

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
No plans.  Function.prototype.apply is still there, albeit not as  
powerful or convenient.

I'm still hoping for something in ES4 that makes apply universal (it  
doesn't work on alert in IE; this should not be allowed by the spec  
by leaving it up to "host objects" to be broken if they ought to  
delegate to Function.prototype).  It would also be helpful for  
bootstrapping if new and apply could be composed; this came up when I  
wrote Narcissus.  Suggestions?

/be


On Nov 9, 2006, at 12:14 PM, Erik Arvidsson wrote:

> Is argument unpacking like in Python planned?
>
> See http://docs.python.org/tut/node6.html#SECTION006740000000000000000
> for an introduction.
>
> --
> erik
> _______________________________________________
> Es4-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es4-discuss


Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
I should mention that destructuring formal parameters allow the  
function to take an array actual parameter and bind its elements to  
named formal parameters:

function f(a, [b, c]) { print(a, b, c); }
f(1, [2, 3]) // prints "1 2 3"

This is different yet again.  I wanted to point it out in case it was  
missed (it wasn't specified in the first wiki dump, IIRC).  It's  
implemented in JS1.7 in Firefox 2.

/be

On Nov 9, 2006, at 1:29 PM, Brendan Eich wrote:

> No plans.  Function.prototype.apply is still there, albeit not as  
> powerful or convenient.
>
> I'm still hoping for something in ES4 that makes apply universal  
> (it doesn't work on alert in IE; this should not be allowed by the  
> spec by leaving it up to "host objects" to be broken if they ought  
> to delegate to Function.prototype).  It would also be helpful for  
> bootstrapping if new and apply could be composed; this came up when  
> I wrote Narcissus.  Suggestions?
>
> /be

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

P T Withington
In reply to this post by Brendan Eich-2
On 2006-11-09, at 16:29 EST, Brendan Eich wrote:

> It would also be helpful for bootstrapping if new and apply could  
> be composed; this came up when I wrote Narcissus.  Suggestions?

Make new a class method rather than a keyword?  (I know, too  
controversial.  But that is the Lisp answer.)

The thing I most miss about apply is that I cannot have 'spread args':

foo.apply(bar, bletch, crud, rest) === foo.apply(bar, [bletch,  
crud].concat(rest))

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

@lnicodon


On 11/9/06, P T Withington <[hidden email]> wrote:
On 2006-11-09, at 16:29 EST, Brendan Eich wrote:

> It would also be helpful for bootstrapping if new and apply could
> be composed; this came up when I wrote Narcissus.  Suggestions?

Make new a class method rather than a keyword?  (I know, too
controversial.  But that is the Lisp answer.)

Well, some workaround do exists within the current language

<a href="http://groups.google.com/group/netscape.public.mozilla.jseng/msg/48395a68e716c9d0" target="_blank" onclick="return top.js.OpenExtLink(window,event,this)"> http://groups.google.com/group/netscape.public.mozilla.jseng/msg/48395a68e716c9d0

they just won't scale to some host objects, (and, yes, they incur some memory overhead, even though a constant one).
So while I like the lisp way, it will unfortunately not accomodate those alien objects.

The thing I most miss about apply is that I cannot have 'spread args':

foo.apply(bar, bletch, crud, rest) === foo.apply(bar, [bletch,
crud].concat(rest))

Is

foo.call(bar, bletch, crud, rest)

an answer, or did I miss the point ?

Cheers,

Alexis
Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
In reply to this post by P T Withington
On Nov 9, 2006, at 2:19 PM, P T Withington wrote:

> On 2006-11-09, at 16:29 EST, Brendan Eich wrote:
>
>> It would also be helpful for bootstrapping if new and apply could  
>> be composed; this came up when I wrote Narcissus.  Suggestions?
>
> Make new a class method rather than a keyword?  (I know, too  
> controversial.  But that is the Lisp answer.)

We could expose intrinsic::construct as the meta-object protocol hook  
underlying new, and allow you to write, e.g.:

   class Complex! {
     static intrinsic function construct(real:double,  
imag:double):Complex {...}
     . . .
   }

Then you could always invoke 'Complex.intrinsic::construct.apply
(Complex, args)' where args is [real, imag]. Or something simpler to  
write (static intrinsic function my summer vacation was very  
nice ;-), but this example is in keeping with the proposed call hook  
(https://intranet.mozilla.org/ECMA/wiki/doku.php?
id=proposals:builtin_classes).  Is this what you mean?

/be

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
On Nov 9, 2006, at 2:47 PM, Brendan Eich wrote:

> We could expose intrinsic::construct as the meta-object protocol  
> hook underlying new, and allow you to write, e.g.:
>
>   class Complex! {
>     static intrinsic function construct(real:double,  
> imag:double):Complex {...}
>     . . .
>   }
>
> Then you could always invoke 'Complex.intrinsic::construct.apply
> (Complex, args)' where args is [real, imag]. Or something simpler  
> to write (static intrinsic function my summer vacation was very  
> nice ;-), but this example is in keeping with the proposed call  
> hook (https://intranet.mozilla.org/ECMA/wiki/doku.php?
> id=proposals:builtin_classes).

I see in https://intranet.mozilla.org/ECMA/wiki/doku.php?
id=proposals:builtin_classes (the Object class at least) a 'static  
function new Object() {...}'.  This is, I think, obsolete syntax --  
we do not want to require the class name to be restated after 'new'  
or 'call'.  It may be that we agreed to use 'construct', not 'new',  
as the MOP hook name, since 'new' is reserved and we don't want to  
outlaw direct calls of the static method.

We'll get all this sorted out at next week's TG1 meeting, but the  
upshot seems to be that a static (intrinsic, I think) method for  
constructing instances of a class will be provided by the runtime,  
and it can be overridden to give custom constructor behavior.  Anyone  
see a problem with this approach?

/be


Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
On Nov 9, 2006, at 2:54 PM, Brendan Eich wrote:

> On Nov 9, 2006, at 2:47 PM, Brendan Eich wrote:
>
>> We could expose intrinsic::construct as the meta-object protocol  
>> hook underlying new, and allow you to write, e.g.:
>>
>>   class Complex! {
>>     static intrinsic function construct(real:double,  
>> imag:double):Complex {...}
>>     . . .
>>   }
>>
>> Then you could always invoke 'Complex.intrinsic::construct.apply
>> (Complex, args)' where args is [real, imag]. Or something simpler  
>> to write (static intrinsic function my summer vacation was very  
>> nice ;-), but this example is in keeping with the proposed call  
>> hook (https://intranet.mozilla.org/ECMA/wiki/doku.php?
>> id=proposals:builtin_classes).
>
> I see in https://intranet.mozilla.org/ECMA/wiki/doku.php?
> id=proposals:builtin_classes (the Object class at least) a 'static  
> function new

Sorry for the private links.  See http://developer.mozilla.org/es4/ 
proposals/builtin_classes of course.

> Object() {...}'.  This is, I think, obsolete syntax -- we do not  
> want to require the class name to be restated after 'new' or  
> 'call'.  It may be that we agreed to use 'construct', not 'new', as  
> the MOP hook name, since 'new' is reserved and we don't want to  
> outlaw direct calls of the static method.

"... we don't want to outlaw explicit calls [and apply's]", I should  
have written.  The whole point is that you can invoke  
C.intrinsic::construct directly or indirectly via .apply/.call, of  
course (direct/indirect != explicit/implicit).  The implicit call to  
intrinsic::construct via operator new could call any old hook-name.

BTW, we use the intrinsic namespace to avoid __call__ or  
__construct__ style mangled names while providing early-binding  
opportunities to optimizing implementations, without colliding with  
existing plain-named 'call' or 'construct' ad-hoc properties in the  
installed base.

/be

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

P T Withington
In reply to this post by Brendan Eich-2
On 2006-11-09, at 17:47 EST, Brendan Eich wrote:

> On Nov 9, 2006, at 2:19 PM, P T Withington wrote:
>
>> On 2006-11-09, at 16:29 EST, Brendan Eich wrote:
>>
>>> It would also be helpful for bootstrapping if new and apply could  
>>> be composed; this came up when I wrote Narcissus.  Suggestions?
>>
>> Make new a class method rather than a keyword?  (I know, too  
>> controversial.  But that is the Lisp answer.)
>
> We could expose intrinsic::construct as the meta-object protocol  
> hook underlying new, and allow you to write, e.g.:
>
>   class Complex! {
>     static intrinsic function construct(real:double,  
> imag:double):Complex {...}
>     . . .
>   }
>
> Then you could always invoke 'Complex.intrinsic::construct.apply
> (Complex, args)' where args is [real, imag]. Or something simpler  
> to write (static intrinsic function my summer vacation was very  
> nice ;-), but this example is in keeping with the proposed call  
> hook (https://intranet.mozilla.org/ECMA/wiki/doku.php?
> id=proposals:builtin_classes).  Is this what you mean?

Yes.  In our implementation `new x(...)` is just syntactic sugar for  
`x.make(...)`.  (And make, rather than magically acquiring `this` as  
constructors do, uses `super.make.apply(arguments)`.)

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

P T Withington
In reply to this post by @lnicodon
On 2006-11-09, at 17:45 EST, @lnicodon wrote:

> The thing I most miss about apply is that I cannot have 'spread args':
>>
>> foo.apply(bar, bletch, crud, rest) === foo.apply(bar, [bletch,
>> crud].concat(rest))
>
>
> Is
>
> foo.call(bar, bletch, crud, rest)
>
> an answer, or did I miss the point ?

My example was poor.  Replace `rest` with `[some, more, arguments]`.

What I'd like to say:

   foo.apply(bar, bletch, crud, [some, more, arguments])

What I have to say now:

   foo.apply(bar, [bletch,crud].concat([some, more, arguments]))

I.e., I wish apply took any number of arguments, where the last  
argument was an array that would be 'spread' into the call:

   foo.call(bar, bletch, crud, some, more, arguments)



Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Johan Sundström
Pardon a somewhat overly long message on a stray issue, but I want to
warn against (however well meaning) syntactic sugar introduced in this
manner; it's been done before and often ends up doing a lot more harm
than good. If you have already spotted the culprit, you need not read
on.

On 11/10/06, P T Withington <[hidden email]> wrote:
> My example was poor.  Replace `rest` with `[some, more, arguments]`.
>
> What I'd like to say:
>
>    foo.apply(bar, bletch, crud, [some, more, arguments])

Imagine you wanted to pass the literal argument array ["bletch",
"crud", ["some", "more", "arguments"]] (yes, the third argument being
an array) to a bar.foo which takes three arguments. It could of course
still be done, by way of either of these syntaxes:

   foo.apply(bar, "bletch", "crud", ["some", "more", "arguments"], []);
   foo.apply(bar, "bletch", "crud", [["some", "more", "arguments"]]);

...but I would guess the most common case in practice would be
unsuspecting programmers just passing their third variable blotch
(containing ["some", "more", "arguments"], run time), arriving at
bar.foo like the call

  bar.foo( "bletch", "crud", "some" );

...for all practical purposes, since this version of the function
doesn't look at any later arguments. With luck, it breaks loudly for
receiving a string where it was intended to have received an array,
but Murphy says it'll fail silently until the code has been deployed
in the wild.

> What I have to say now:
>
>    foo.apply(bar, [bletch,crud].concat([some, more, arguments]))
>
> I.e., I wish apply took any number of arguments, where the last
> argument was an array that would be 'spread' into the call:
>
>    foo.call(bar, bletch, crud, some, more, arguments)

Are you really sure that would be a good idea in practice? To cater
the case where the last argument passed to foo may sometimes actually
contain an array value, the idiomatic apply call signature would
always have to be foo.apply( bar, all, args, here, [] ) where you
*don't* use this feature, or that last "here" may end up unpacked
across a few extra arguments when it was an array, so foo's third
argument actully ends up here[0] rather than what the here variable
contained.

It looks like typical syntactic sugar for one case turning into
syntactic rat poison for all other cases to me, much like how array
literal and new Array() notation is anomalous for the case where the
constructor gets passed a single integer attribute (in at least
several implementations of javascript -- I am not sure whether it
applies to ecmascript 4 too);

new Array("high", 5);
=> ["high", 5]
new Array("hi");
=> ["hi"]
new Array(5);
=> [undefined, undefined, undefined, undefined, undefined]

Introducing a proper unpack operator would more safely avoid the
syntactic overhead you dislike, without adversely affecting other
cases, but I would rather keep the pain than defend future code
against the proposed feature. It would end up just another one of
those dangerous backwaters of the language where only the top few
percentiles would go safe and those advanced enough to have found the
apply but not its quirks, would be infested with difficult-to-find
bugs. Worse still, not always, only in a potentially uncommon case
dependent on the runtime type of the last function argument passed.

--
 / Johan Sundström, http://ecmanaut.blogspot.com/


Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Brendan Eich-2
Completely agree, it's hard to DWIM if array actual parameters get  
auto-unpacked without the callee's formal parameter list saying so  
(destructuring formal parameters).  The better design bias IMHO is to  
make callers who want an array actual to be unpacked do more work, be  
explicit about what they want.  So I'm actually in favor of requiring  
something like

   foo.apply(bar, [bletch,crud].concat([some, more, arguments]))

Of course it sucks not to be able to use an operator for array  
concatenation, but that's fixable in ES4 with operators (tangent:  
should we do that in the standard?  E.g., map function +(a:Array,  
b:Array):Array onto concat?).

/be

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

P T Withington
In reply to this post by Johan Sundström
On 2006-11-10, at 15:21 EST, Johan Sundström wrote:

[...]

> On 11/10/06, P T Withington <[hidden email]> wrote:
>> My example was poor.  Replace `rest` with `[some, more, arguments]`.
>>
>> What I'd like to say:
>>
>>    foo.apply(bar, bletch, crud, [some, more, arguments])
>
> Imagine you wanted to pass the literal argument array ["bletch",
> "crud", ["some", "more", "arguments"]] (yes, the third argument being
> an array) to a bar.foo which takes three arguments. It could of course
> still be done, by way of either of these syntaxes:
>
>   foo.apply(bar, "bletch", "crud", ["some", "more", "arguments"], []);
>   foo.apply(bar, "bletch", "crud", [["some", "more", "arguments"]]);

Then I would not use `apply`.  I would use `call`.

foo.call(bar, "bletch", "crud", ["some", "more", "arguments"]);

This is exactly the difference between `call` and `apply`.  `apply`  
takes an Array and 'spreads' the arguments out to `call`.  All I was  
asking is to amend `apply` to allow you to specify additional spread  
arguments before the Array.

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Peter Hall-2
Allowing additional arguments before the array is confusing. And
aren't you just as likely to want to pass additional arguments after
the array? Using an overridden "+" operator - or concat() for that
matter - doesn't seem like much of a burden.

Also, unless I'm missing something, the language wouldn't be able to
enforce the type of that last array argument any more. So, while this
feature would be backwards compatible, it would mean losing something
and, even though that thing is small, it doesn't look like much is
being gained either.

Peter

On 11/12/06, P T Withington <[hidden email]> wrote:

> On 2006-11-10, at 15:21 EST, Johan Sundström wrote:
>
> [...]
>
> > On 11/10/06, P T Withington <[hidden email]> wrote:
> >> My example was poor.  Replace `rest` with `[some, more, arguments]`.
> >>
> >> What I'd like to say:
> >>
> >>    foo.apply(bar, bletch, crud, [some, more, arguments])
> >
> > Imagine you wanted to pass the literal argument array ["bletch",
> > "crud", ["some", "more", "arguments"]] (yes, the third argument being
> > an array) to a bar.foo which takes three arguments. It could of course
> > still be done, by way of either of these syntaxes:
> >
> >   foo.apply(bar, "bletch", "crud", ["some", "more", "arguments"], []);
> >   foo.apply(bar, "bletch", "crud", [["some", "more", "arguments"]]);
>
> Then I would not use `apply`.  I would use `call`.
>
> foo.call(bar, "bletch", "crud", ["some", "more", "arguments"]);
>
> This is exactly the difference between `call` and `apply`.  `apply`
> takes an Array and 'spreads' the arguments out to `call`.  All I was
> asking is to amend `apply` to allow you to specify additional spread
> arguments before the Array.
> _______________________________________________
> Es4-discuss mailing list
> [hidden email]
> https://mail.mozilla.org/listinfo/es4-discuss
>


Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Dave Herman-2
> Also, unless I'm missing something, the language wouldn't be able to
> enforce the type of that last array argument any more. So, while this

In ES3 there's no type enforcement, and I wouldn't count on being able to
come up with a type for apply anyway. I'm not saying I agree with one side
or another on this point, but I'm not sure our type system can express
useful type constraints on apply. I believe it's a dependently typed
function.

Dave

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Peter Hall-2
ES3 has little to do with it. In ES4, apply's signature would
currently be something like:

     function apply(target:Object, args:Array):*;

And there would be compile-time and run-time errors if args was not an
Array. With P T Withington's suggestion, it would be:

     function apply(target:Object, ...rest):*;

in which case, there would be no compile-time errors and run-time
errors would depend on the implementation.

Agreed that apply() is intrinsically dynamic, but there can be some
constaints on how it is used, and those can be enforced by the
language.

Peter

On 11/12/06, David A. Herman <[hidden email]> wrote:

> > Also, unless I'm missing something, the language wouldn't be able to
> > enforce the type of that last array argument any more. So, while this
>
> In ES3 there's no type enforcement, and I wouldn't count on being able to
> come up with a type for apply anyway. I'm not saying I agree with one side
> or another on this point, but I'm not sure our type system can express
> useful type constraints on apply. I believe it's a dependently typed
> function.
>
> Dave
>

Reply | Threaded
Open this post in threaded view
|

Re: Argument unpacking?

Dave Herman-2
> ES3 has little to do with it.

It does insofar as your point was that we lose something by making
`apply' harder to type: since `apply' has never had a type, we haven't
lost anything per se.

But I see what you're saying: it's an "opportunity cost" since it
becomes less ES4-typeable.

>     function apply(target:Object, args:Array):*;

..which is a pretty weak type.

A more precise type for such a function would be something like:

     function.<T1, ..., Tn, Trest, Tres>
         apply(fun:function(x1:T1, ..., xn:Tn, ...rest:Trest):Tres,
               args:[T1, ..., Tn, Trest]):Tres

We can almost express this type in our proposed type system (I think I
was wrong about this being a dependent type). It's just a generic type,
but one with a variable number of type parameters, which we can't handle.

> And there would be compile-time and run-time errors if args was not an
> Array. With P T Withington's suggestion, it would be:
>
>     function apply(target:Object, ...rest):*;

[As a curious side note: we could *almost* express the type of the Lispy
version of `apply'--i.e., a sequence of arguments of type * followed by
a single argument of type Array--by giving the rest-args a structural
array type; see

http://developer.mozilla.org/es4/proposals/structural_types_and_typing_of_initializers.html

for a sketch. That page doesn't explain very much but the idea is that
array types can have finite heterogeneous prefixes--e.g. the type of
arrays whose first element is a Boolean, second element is a String, and
remaining elements are ints. So we can express the types of a finite
prefix followed by an unbounded suffix, but we can't express the types
of an unbounded prefix followed by a finite suffix, which is what we
would need in this case.]

> in which case, there would be no compile-time errors and run-time
> errors would depend on the implementation.

Even if it doesn't have a precise type, the library spec can still
impose constraints on implementations so it isn't
implementation-specific. You're right about compile-time errors, though.

> Agreed that apply() is intrinsically dynamic, but there can be some
> constaints on how it is used, and those can be enforced by the
> language.

Sure, although it's a modest amount of checking. The trade-off is
between a rather minor convenience in error checking versus a similarly
minor convenience in flexibility of use.

Dave