Closed dead-claudia closed 8 years ago
@zenparsing What do you think? If we change the syntax for this
binding, this could be easily changed later.
Thanks for the detailed work here. I think it's probably best to leave this out for now (minimal proposals have a better chance of getting committee support), but this is a cool extension of the syntax and it would be nice to have.
I'm not super keen on the ::MyClass[new]
syntax, though. I wonder if we can do something like:
MyClass::new;
// More or less:
MyClass::function(...args) { return new this(...args) };
I'm just thinking out loud here, but I think we'd have to restrict the right hand side such that it can't be new MemberExpression
or new NewExpression
.
In general, I don't like those kind of special case grammar rules, but maybe it's worth it in this case.
I'll agree with that, but it may have to change if we go with option 1 in my comparison, as it conflicts with what would currently be ::obj.new
(which is a perfectly valid property, semantically no different than obj.foo
or obj.be234f09cjs3knr1
). Like I said, it can be changed later. I'll make the appropriate edit now.
@zenparsing You okay with how this looks?
@isiahmeadows I'd like to work this in, but in a slightly different way. I'm going to push a change which adds lookahead restrictions to the grammar, and then we can start working from there.
I don't want to create a new type of exotic bound function. Rather, I'd like to create some sort of built-in function object which basically just does:
this
value.true
.And which does not have a [[Construct]] internal method.
The runtime semantics for
BindExpression :
LeftHandSideExpression :: new
Would verify that the left operand IsConstructor
, create an instance of that built-in function and then do the BoundFunctionCreate
against it like in the other forms. It would set the "length" and "name" properties based on the wrapped constructor though.
What do you think?
__proto__
)? FWIW normal binding does result in a function that (redundantly) implements [[Construct]] if the target implements it, since it uses BoundFunctionCreate, so would you like me to create a new version to special-case the whole thing?I went ahead and made the constructor version only callable and without a prototype (to make it internally consistent). I do feel that the other should be made similarly.
Also, the left operand is checked to be a constructor, unless I majorly overlooked something. And I disagree that BoundFunctionCreate should be used. It's like using a sledgehammer to pound in a small nail. There's never any arguments, and the ability to call them depends on their type.
It's redundant, and removing that ability allows for a variety of optimizations. It will become almost weightless to bind functions and constructors, but by using BoundFunctionCreate, many of those optimizations are partially obscured and/or limited. Also, checking the type at creation (throwing a TypeError if they aren't right) would lead to better engine optimization of it and more type-safe code.
I believe the prototype of a bound constructor should be Function.prototype
.
I was trying to minimize the difference between X::new
and the other forms by re-using BoundFunctionCreate
. I'm not particularly worried about implementation burden, but there is another way it could be done, and which might be more clear to the reader.
We could define a new built-in anonymous function (see http://www.ecma-international.org/ecma-262/6.0/#sec-promise-reject-functions for an example) which would have a [[TargetConstructor]] internal slot, and would just perform Construct
against it. The runtime semantics would create an instance of this anonymous function, set the [[TargetConstructor]] internal slot, and then set the "name" and "length" properties like in the other forms.
See http://www.ecma-international.org/ecma-262/6.0/#sec-createresolvingfunctions for an example of creating an anonymous function instance.
If we go that route, there's little benefit to just creating our own
closure. Also, [[Construct]] doesn't have the standard properties of a
function, because it's not an actual ECMAScript function complete with
name
and length
. I had to special case the constructor.
And when I referred to the prototype, I meant the prototype
property, not
the own __proto__
property of the function. In this sense, arrow
functions don't have a prototype, and always having the Function prototype
doesn't make sense in this context.
On Wed, Dec 9, 2015, 09:47 zenparsing notifications@github.com wrote:
I believe the prototype of a bound constructor should be Function.prototype.
I was trying to minimize the difference between X::new and the other forms by re-using BoundFunctionCreate. I'm not particularly worried about implementation burden, but there is another way it could be done, and which might be more clear to the reader.
We could define a new built-in anonymous function (see http://www.ecma-international.org/ecma-262/6.0/#sec-promise-reject-functions for an example) which would have a [[TargetConstructor]] internal slot, and would just perform Construct against it. The runtime semantics would create an instance of this anonymous function, set the [[TargetConstructor]] internal slot, and then set the "name" and "length" properties like in the other forms.
See http://www.ecma-international.org/ecma-262/6.0/#sec-createresolvingfunctions for an example of creating an anonymous function instance.
— Reply to this email directly or view it on GitHub https://github.com/zenparsing/es-function-bind/pull/28#issuecomment-163269403 .
it's not an actual ECMAScript function complete with
name
andlength
Right, so you can't actually pull it off and hand it to BoundConstructorCreate or whatever. The name
and length
properties would come from the actual constructor function object.
Non-constructor functions don't have a "prototype" property. Being an anonymous built-in function takes care of that.
@zenparsing
And as far as I can tell in the ES6 spec, only the user-visible anonymous functions have a length specified. And some aren't visible to ECMAScript code at all, such as MakeArgGetter and MakeArgSetter. To be honest, the way I have [[Call]] defined for bound constructors is much like an anonymous function, anyways, even though I don't actually use the term. Or in JS pseudocode, here's basically the way I have it spec'd:
function %BoundConstructorCreate(targetFunction) {
const obj = {}
%SetInternal(obj, "[[Call]]", function (thisArgument, argumentsList) {
return targetFunction.[[Construct]](argumentsList, target)
})
%SetInternal(obj, "[[Extensible]]", true)
return obj
}
// BindExpression:
// LeftHandSideExpression[?Yield] :: new
function %createBoundConstructor(target) {
// Assert: we already have the reference from parsing
if (!%IsConstructible(target)) {
throw new TypeError("target must be constructible")
}
const F = %BoundConstructorCreate(target)
let L = 0
if (%HasOwnProperty(target, "length")) {
let targetLen = target.length
if (typeof targetLen === "number") L = %ToInteger(targetLen)
}
%DefinePropertyOrThrow(F, "length", PropertyDescriptor({
value: L,
writable: false,
enumerable: false,
configurable: true,
}))
let targetName = target.name
if (typeof name !== "string") targetName = ""
%SetFunctionName(F, targetName, "bound")
return F
}
Since these bound factories don't have [[Construct]], there's really no need for an exotic object. And I want to keep this proposal lightweight.
Basically, just replace your call to BoundConstructorCreate
with the creation and initialization of the anonymous function.
@zenparsing Okay. I see what you're thinking. Something to the effect of this:
A bound constructor wrapper function is an anonymous built-in function with a [[BoundConstructorFunction]] internal slot.
When a bound constructor wrapper function F is called with 0 or more args, it performs the following steps:
Yep, that looks about right. Some little stuff:
@isiahmeadows can you rebase? i'll add comments to the diff when you're ready.
Sure...I've had to do it once already.
@zenparsing Done.
Left some comments. Thanks for refactoring out the "length" and "name" setting!
Also, in my most recent commit, I've switch to using the ? shorthand, (e.g. "Let status be ? GetValue(...)"). It's a new shorthand for doing the ReturnIfAbrupt thing. I noticed that some of those changes are reverted in your diff. Can we keep those shorthands?
Sure. I'll just make a note of it above, as it isn't obvious at first sight.
Thanks for putting up with all of the nitpicks : )
Thanks for putting up with all of the nitpicks : )
@zenparsing No problem
Looks good. Now squash these commits into one and I'll pull it in!
@zenparsing Squashed and pushed (pun totally intended :smile:)
Merged - thanks!
@zenparsing Welcome!
Brought up in passing in #26, but was never actually in the spec (feature more important than syntax).
I believe this works similarly to
::obj.foo
, but specifically calls the constructor, as in::Class[new]
. It's unambiguously the constructor, and would fail in any other context.