super.apply(this, arguments)
Interesting. One wonders why we keep the silly fiction of naming the
constructor after the class if it is really going to show up in the
class as the construct
method. It's always bothered me that I have
to write the class name in two places, and fix it in two places if I
change my mind, or copy/paste a class to create a new class.
But, does the construct method cause allocation? If so, it doesn't
really solve my problem, since I really only want the initialization
of the superclass constructor, not allocation.
It seems to me that what I really want is for there to be separate
ways to apply new
(to allocate a new instance using apply) and to
apply the constructor method (to initialize a new instance using apply).
I want to be able to say:
class Foo extends Bar {
function Foo () { super.initialize.apply(this, arguments);
I don't want to call super.construct
because I don't want to
allocate a new instance of my superclass, I just want to run the
initialization that my superclass does.
I suppose I have to say:
Bar.apply(this, arguments);
but now I have to type not only my class name, but also my superclass
name in two places.
Or maybe we are going to be told this is yet another outdated wiki
page that is leading us astray?
Wiki rot. The construct method is long since obsolete.
The wiki really is wildly out of date in some respects. There is nothing solid to replace it right now, but reading it can cause severe confusion...
Ok. I didn't look in the wiki to start with because of that, hence I
ask on the list: can I apply my super's constructor as in the subject
line?
Sorry, but you can't.
On 12/20/07 7:20 AM, P T Withington wrote:
Ok. I didn't look in the wiki to start with because of that, hence I ask on the list: can I apply my super's constructor as in the subject line?
Jeff Dyer wrote:
Sorry, but you can't.
That's a shame.
Sorry, I didn't mean to sound so flippant.
As you may know, super expressions limit the binding of the name after the dot to the base class bindings. So the fragment above would call the super instance method 'apply', not the super constructor. We could give 'apply' some special meaning when the first argument is 'this', but the super constructor will have already been called during initialization.
None of this means that the desired semantics wouldn't be possible to achieve with another syntax. My guess is that it would be somehow similar to the current super initializer syntax, which looks like this:
class A extends B { function A(x,y,z) : super (x,y,z) { } }
Suggestion welcomed.
I haven't tried this in the RI, but what about:
var sup = super; sup.apply(this, arguments);
?
Peter
Peter Hall wrote:
I haven't tried this in the RI, but what about:
var sup = super; sup.apply(this, arguments);
No, the problem arises from two separate facts:
- constructor invocation is not a function call
- super-constructor chaining is not a function call either
Both of these are different operations with different semantics. We have a way of getting a function for the former (using the reflection API) such that you can run a constructor with variable args using apply. We do not have a way of getting a function for the latter, and really should not. Super-constructor chaining occurs only in a special, limited syntactic and semantic context: the end of the settings list, before the constructor body is entered. There's no reasonable interpretation of a first class escaping function that does this, as the context in which the function even makes sense vanishes as soon as the constructor body is entered. This is why we currently just use an explicit super-expression.
However, I think we can all see a nice symmetry argument that it would be nice to have an additional form for passing variable arguments up to your super-constructor. I'd imagine lots of possibilities might work, so long as they're unambiguous:
function myObj(...args)
: super.apply(args) // a pseudo-property?
{}
function myObj(...args)
: super_apply(args) // a pseudo-global?
{}
function myObj(...args)
: super_arguments = args // a pseudo-local?
{}
I suspect the first will turn the least number of stomachs, and is probably ok if we prohibit all other super-property accesses in the settings list. This seems plausible to me given that the first-class super rvalue hasn't even been initialized by the time the settings run, only allocated.
Given that we already have a whacky syntax for calling the super
initializer (which I confess I had forgotten), why not allow apply
there? It's unambiguous there, since you are only allowed to call the
super initializer. This syntax is just letting you override how your
arguments are passed on to your super initializer. What is the
default if I don't specify a call to my super initializer? Does it
get all my arguments, or none of them?
class A extends B { function A(x,y,z) : super.apply (this, arguments) { ... } }
?
Personally, I really don't like the magic of the class name naming the
initializer. I would rather see a magic method name, say
initialize
, so you say:
class A extends B { function initialize(x,y,z) { super.initialize.apply(this, arguments); ... } }
but that is probably too radical. On the plus side, you don't have to
repeat the class name, which makes easier maintenance and code reuse,
on the minus side there is a magic method name and you have to decide
whether to enforce arglist congruency or not (an issue hidden by
having each initializer have the class name as its name).
On 12/20/07 11:29 AM, P T Withington wrote:
Given that we already have a whacky syntax for calling the super initializer (which I confess I had forgotten), why not allow apply there? It's unambiguous there, since you are only allowed to call the super initializer. This syntax is just letting you override how your arguments are passed on to your super initializer.
The only problem I see is that the syntax 'super . apply ( )" looks a lot like a super expression, which has a different meaning than what you want it to mean. Not a deal breaker, but an usability check.
What is the
default if I don't specify a call to my super initializer? Does it get all my arguments, or none of them?
None of them.
class A extends B { function A(x,y,z) : super.apply (this, arguments) { ... } }
?
The original Netscape ES4 proposal had syntax for passing arguments as an array. (Waldemar are you listening?) IIRC it used triple dots like this:
foo(...args)
to mean use the elements of 'args' as positional arguments of foo. We dropped this from AS3 for lack of evidence for its need. But this might be a way forward.
class A extends B { function A(x,y,z) : super(...arguments) { } }
Personally, I really don't like the magic of the class name naming the initializer. I would rather see a magic method name, say
initialize
, so you say:class A extends B { function initialize(x,y,z) { super.initialize.apply(this, arguments); ... } }
but that is probably too radical. On the plus side, you don't have to repeat the class name, which makes easier maintenance and code reuse, on the minus side there is a magic method name and you have to decide whether to enforce arglist congruency or not (an issue hidden by having each initializer have the class name as its name).
During the development of AS3 we considered other naming schemes for class constructors. In the end we chose the status quo because it is familiar (least surprise) and constructors really aren't methods that can be called directly. They are blocks of code that get executed in conjunction with the new operation. Making them look like methods seems misleading, at least in this language.
On Dec 20, 2007 11:29 AM, P T Withington <ptw at pobox.com> wrote:
Given that we already have a whacky syntax for calling the super initializer (which I confess I had forgotten), why not allow apply there? It's unambiguous there, since you are only allowed to call the super initializer. This syntax is just letting you override how your arguments are passed on to your super initializer. What is the default if I don't specify a call to my super initializer? Does it get all my arguments, or none of them?
class A extends B { function A(x,y,z) : super.apply (this, arguments) { ... } }
?
Personally, I really don't like the magic of the class name naming the initializer. I would rather see a magic method name, say
initialize
, so you say:class A extends B { function initialize(x,y,z) { super.initialize.apply(this, arguments); ... } }
but that is probably too radical. On the plus side, you don't have to repeat the class name, which makes easier maintenance and code reuse, on the minus side there is a magic method name and you have to decide whether to enforce arglist congruency or not (an issue hidden by having each initializer have the class name as its name).
So you're proposing making some sort of "instance constructor" property on the class
MyClass.instanceConstructor.newApply(lockable, args );
Would you be able to call that with another object?
function LockableFile() { var lockable = Lockable.createInstance(); var lockableFile = FIle.instanceConstructor.newApply(lockable, args); }
What about calling a nested class?
class Menu() { class Item() {
} function buildSingleItemMenu(...args) { this.new Item(...args); }
function buildSingleItemMenu( ) { return new Menu().Item.instanceConstructor.newApply( arguments ); }
Just some ideas.
On 2007-12-20, at 13:05 EST, Jeff Dyer wrote:
Sorry, I didn't mean to sound so flippant.
As you may know, super expressions limit the binding of the name after the dot to the base class bindings. So the fragment above would call the super instance method 'apply', not the super constructor.
Right, because the super is a Function, which gets an apply method from Function.prototype.
We could give
'apply' some special meaning when the first argument is 'this', but the super constructor will have already been called during initialization.
None of this means that the desired semantics wouldn't be possible to achieve with another syntax. My guess is that it would be somehow similar to the current super initializer syntax, which looks like this:
class A extends B { function A(x,y,z) : super (x,y,z) { } }
Suggestion welcomed.
I kind of like the syntax P T Withington suggests for reasons he mentioned but I don't like the name | initialize | to refer to the instance constructor.
Garrett
On Dec 20, 2007, at 4:01 PM, Jeff Dyer wrote:
The original Netscape ES4 proposal had syntax for passing arguments
as an array. (Waldemar are you listening?) IIRC it used triple dots like
this:foo(...args)
to mean use the elements of 'args' as positional arguments of foo. We dropped this from AS3 for lack of evidence for its need.
Wow. I like this, never saw it in Waldemar's proposals, but of course
Python and other languages have something akin. This is the missing
solution that satisfies the demand for compositional new and apply,
as well as other apply-like use-cases (see the thread on this very
list, inspired by Python, starting here).
Presumably you could supply one or more positional actual parameters
before the "packed" parameter, as Python allows:
def foo(a,b,c): ... print a,b,c ...
foo(1,2,3)
1 2 3
a = [1,2,3] foo(*a)
1 2 3
b = [2,3] foo(1,*b)
1 2 3
This beats a super.apply special form any day!
On 21/12/2007, at 11:01 AM, Jeff Dyer wrote:
We dropped this from AS3 for lack of evidence for its need.
Really? The "rest" arguments (tail arguments specified as an array)
is available in AS3 as far as I'm aware:
function foo(first:*, second:*, ...rest) {
trace(arguments.length); // variable int, depending on how
foo is called trace(rest is Array); // true }
Isn't this also in the ES4 overview (page 22)?
Cheers,
-- Nathan de Vries
That is a slightly different feature. ...rest is included for declaring variable length argument lists. But not for passing a variable number of values when you actually make the call.
// this is implemented in AS3 function myFunction(...rest){ }
// this is not args = [1,2,3]; myFunction(...args);
Peter
On 2007-12-21, at 00:39 EST, Brendan Eich wrote:
On Dec 20, 2007, at 4:01 PM, Jeff Dyer wrote:
The original Netscape ES4 proposal had syntax for passing arguments
as an array. (Waldemar are you listening?) IIRC it used triple dots like
this:foo(...args)
to mean use the elements of 'args' as positional arguments of foo. We dropped this from AS3 for lack of evidence for its need.
Wow. I like this, never saw it in Waldemar's proposals, but of
course Python and other languages have something akin. This is the
missing solution that satisfies the demand for compositional new and
apply, as well as other apply-like use-cases (see the thread on this
very list, inspired by Python, starting here).Presumably you could supply one or more positional actual parameters
before the "packed" parameter, as Python allows:def foo(a,b,c): ... print a,b,c ...
foo(1,2,3) 1 2 3
a = [1,2,3] foo(*a) 1 2 3
b = [2,3] foo(1,*b) 1 2 3
This beats a super.apply special form any day!
Cute! So ... is the 'spread' operator that spreads an array of
arguments into a parameter list.
On 21/12/2007, at 11:56 PM, P T Withington wrote:
Cute! So ... is the 'spread' operator that spreads an array of
arguments into a parameter list.
Sounds similar to the splat operator of Ruby:
def foo(first, second, *rest)
puts first, second, rest.inspect
end
foo(1,2)
=> 1 2 []
foo(1,2,3,4,5)
=> 1 2 [3, 4, 5]
rest = [3,4,5]
foo(1,2, *rest)
=> 1 2 [3, 4, 5]
rest = [3,4,5]
foo(1,2, rest)
=> 1 2 [[3, 4, 5]]
Cheers,
-- Nathan de Vries
Nathan de Vries wrote:
Sounds similar to the splat operator of Ruby:
def foo(first, second, *rest) puts first, second, rest.inspect end foo(1,2) => 1 2 [] foo(1,2,3,4,5) => 1 2 [3, 4, 5] rest = [3,4,5] foo(1,2, *rest) => 1 2 [3, 4, 5] rest = [3,4,5] foo(1,2, rest) => 1 2 [[3, 4, 5]]
Right, as well as:
rest = [1,2,3,4,5] foo(*rest) => 1 2 [3, 4, 5] all = [0, *rest] => [0, 1, 2, 3, 4, 5]
and... irb(main):001:0> a = [1,2,3] => [1, 2, 3] irb(main):002:0> b,*c = a => [1, 2, 3] irb(main):003:0> b => 1 irb(main):004:0> c => [2, 3] irb(main):005:0> case 2 irb(main):006:1> when 0,*a irb(main):007:1> puts 'yep' irb(main):008:1> end yep => nil
The splat operator splats into argument lists, array literals, etc. It also collects over parameter lists, multiple assignment, cases... I think just about anywhere where ExpressionList is valid. (I'm not sure we want to go that far... just saying.)
Chris
Is this permitted syntax in a constructor:
super.apply(this, arguments);
for the case where I want to pass all my arguments to my superclass
constructor? If not, how does one do that, especially if the
constructor I am calling from takes a ...rest arg?