Function.create
Le 24/09/2011 15:33, Jake Verbaten a écrit :
There is no standardized way to create a new function with a prototype which is not Function.prototype.
I propose Function.create
/* Creates a new function whose prototype is proto. The function body is the same as the function fbody. The hash of propertydescriptors props is passed to
defineproperties just like Object.create does. */ Function.create = (function() { var functionBody = function _getFunctionBody(f) { return f.toString().replace(/.+{/, "").replace(/}$/, ""); }; var letters = "abcdefghijklmnopqrstuvwxyz".split("");
return function _create(proto, fbody, props) { var parameters = letters.slice(0, fbody.length); parameters.push(functionBody(fbody)); var f = Function.apply(this, parameters); f.__proto__ = proto; Object.defineProperties(f, props); return f; }; })();
This is the same as Object.create except the second parameter is a function.
It will create a new function whose function body is the same as the function passed in.
I don't believe this is possible to do in ES5 without proto
It is not indeed. This topic has been discussed a couple of times here. There is one proposal that is more generic than your solution which is the proto operator [1].
Basically, to create a function with a chosen prototype, you can do:
var f = myProto <| function(a, b){return a+b;} Object.getPrototypeOf(f); // myProto f(1, 2); // 3
I'm a big fan of the proto operator proposal, however, as raised previously this operator relies on the object being created to have an intialisation syntax. This prevents, for instance, Date objects to have a custom prototype with this method. I am not very familiar with it yet, but I think that if they ever came to life, ParallelArrays [2] would suffer from the same problem. By the way, could someone add this concern as a note ("open issue" or "limitation" or something like this) in the proto operator page, please?
One even more generic way which would work even for objects that have no initialization syntax would to standardize one of [3] or [4]. Is there a wiki page mentionning these two functions somewhere? If not, may it be added and linked in some way to the proto operator?
David
[1] harmony:proto_operator [2] RiverTrail/RiverTrail/blob/15ae7f6f77d9d2842d9d75458017efd9fe0fbee7/jslib/ParallelArray.js#L29 [3] esdiscuss/2011-March/013141 [4] esdiscuss/2011-March/013154
I don't get the difference between the proto operator and some setPrototype method that could be added (or the actual proto property)...
I believe the goal is to eventually deprecate the magic of proto as mutating the prototype of an object makes it difficult to reason about a program and supporting the mutation of an object's prototype also has a reasonably significant performance cost -- the exact time and place (and extent) of the cost is engine specific, but certainly it is not free.
That said I'm not yet convinced we'll ever be able to kill it off :-/
As a meta-point, I'd like for us to stop adding ".create()" functions, or at least stop adding them without syntax. The early precedent -- document.create() -- is an accident of C++ object ownership semantics from a DOM that often seems to be designed to be explicitly un-idiomatic. We have an allocator form (new) and we have factory functions (Array())…the third creation mechanism seems wholly incongruous.
On Sep 24, 2011, at 10:40 AM, Oliver Hunt wrote:
I believe the goal is to eventually deprecate the magic of proto as mutating the prototype of an object makes it difficult to reason about a program and supporting the mutation of an object's prototype also has a reasonably significant performance cost -- the exact time and place (and extent) of the cost is engine specific, but certainly it is not free.
That said I'm not yet convinced we'll ever be able to kill it off :-/
--Oliver
On Sep 24, 2011, at 10:30 AM, Xavier MONTILLET wrote:
I don't get the difference between the proto operator and some setPrototype method that could be added (or the actual proto property)...
On Sat, Sep 24, 2011 at 6:03 PM, David Bruant <david.bruant at labri.fr> wrote:
Le 24/09/2011 15:33, Jake Verbaten a écrit :
There is no standardized way to create a new function with a prototype which is not Function.prototype.
I propose Function.create
/* Creates a new function whose prototype is proto. The function body is the same as the function fbody. The hash of propertydescriptors props is passed to defineproperties just like Object.create does. */ Function.create = (function() { var functionBody = function _getFunctionBody(f) { return f.toString().replace(/.+{/, "").replace(/}$/, ""); }; var letters = "abcdefghijklmnopqrstuvwxyz".split("");
return function _create(proto, fbody, props) { var parameters = letters.slice(0, fbody.length); parameters.push(functionBody(fbody)); var f = Function.apply(this, parameters); f.__proto__ = proto; Object.defineProperties(f, props); return f; };
})();
This is the same as Object.create except the second parameter is a function.
It will create a new function whose function body is the same as the function passed in.
I don't believe this is possible to do in ES5 without proto It is not indeed. This topic has been discussed a couple of times here. There is one proposal that is more generic than your solution which is the proto operator [1].
Basically, to create a function with a chosen prototype, you can do:
var f = myProto <| function(a, b){return a+b;} Object.getPrototypeOf(f); // myProto f(1, 2); // 3
I'm a big fan of the proto operator proposal, however, as raised previously this operator relies on the object being created to have an intialisation syntax. This prevents, for instance, Date objects to have a custom prototype with this method. I am not very familiar with it yet, but I think that if they ever came to life, ParallelArrays [2] would suffer from the same problem. By the way, could someone add this concern as a note ("open issue" or "limitation" or something like this) in the proto operator page, please?
One even more generic way which would work even for objects that have no initialization syntax would to standardize one of [3] or [4]. Is there a wiki page mentionning these two functions somewhere? If not, may it be added and linked in some way to the proto operator?
David
[1] harmony:proto_operator [2] RiverTrail/RiverTrail/blob/15ae7f6f77d9d2842d9d75458017efd9fe0fbee7/jslib/ParallelArray.js#L29 [3] esdiscuss/2011-March/013141 [4] esdiscuss/2011-March/013154
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
es-discuss mailing list es-discuss at mozilla.org, mail.mozilla.org/listinfo/es-discuss
-- Alex Russell slightlyoff at google.com slightlyoff at chromium.org alex at dojotoolkit.org BE03 E88D EABB 2116 CC49 8259 CF78 E242 59C3 9723
Le 24/09/2011 19:30, Xavier MONTILLET a écrit :
I don't get the difference between the proto operator and some setPrototype method that could be added (or the actual proto property)...
The proto operator works for initialization only on a newly created object while proto allows to change the prototype anytime.
var f = function(){}, a = []; var o = {};
var f2 = o <| function(){}; // ok, new object created var a2 = o <| []; // ok, new object created
var f3 = o <| f; // throw an error, because f is already an object with a prototype. Its prototype can't be changed dynamically var a3 = o <| a; // throws for the same reason
f.proto = o; // prototype changed a.proto = o; // same
What's the use case that justify it's good to change a prototype after the object' initialization?
-----Message d'origine---
Le 24/09/2011 22:00, François REMY a écrit :
What's the use case that justify it's good to change a prototype after the object' initialization?
Excellent question! We've been over this question in the "Standardizing proto" thread starting at [1] and the Firefox bug that triggered it [2]. Besides "subclassing" (which would be now solved with all the things I mentionned before), the only example I have heard of and consider being worth was [3] where there is GUI to create a JS environment. Here, I'm not 100% sure proto is necessary, but it sure is handy to implement changing the prototype of an object dynamically.
David
[1] esdiscuss/2011-March/013131 [2] bugzilla.mozilla.org/show_bug.cgi?id=642500 [3] avocadojs.com
The last example you quoted would perfectly fit in the category "Use a Proxy to emulate the prototype chain, until the 'edited' object is serialized back to source code".
If there's no strong use case that need to be able to set an object prototype to work, we should not ever consider that feature. I really think Proxies provides all the functionnalities required to implement the special border cases where a non-conventionnal behavior is needed.
-----Message d'origine---
On Sat, Sep 24, 2011 at 5:03 PM, David Bruant <david.bruant at labri.fr> wrote:
- Show quoted text -
I defiantly like this <|
operator. I would happily have this instead.
I'm a big fan of the proto operator proposal, however, as raised previously this operator relies on the object being created to have an intialisation syntax. This prevents, for instance, Date objects to have a custom prototype with this method. I am not very familiar with it yet, but I think that if they ever came to life, ParallelArrays [2] would suffer from the same problem. By the way, could someone add this concern as a note ("open issue" or "limitation" or something like this) in the proto operator page, please?
I don't understand the limitation for Date
.
var d = proto <| new Date();
That would appear to just work for me. Of course it would be sensible that proto has Date.prototype somewhere in the chain
One even more generic way which would work even for objects that have no initialization syntax would to standardize one of [3] or [4]. Is there a wiki page mentionning these two functions somewhere? If not, may it be added and linked in some way to the proto operator?
Personally I would be tempted to say that making mutating the prototype chain of an object after creating leads to black magic and abuse.
Le 24/09/2011 22:25, François REMY a écrit :
The last example you quoted would perfectly fit in the category "Use a Proxy to emulate the prototype chain, until the 'edited' object is serialized back to source code".
If there's no strong use case that need to be able to set an object prototype to work, we should not ever consider that feature.
Rest assured that i agree 100% with that statement, I was just answering your question.
I really think Proxies provides all the functionnalities required to implement the special border cases where a non-conventionnal behavior is needed.
I do not think they do on their own, especially when it comes to object identity (equality of objects after a call to Object.getPrototypeOf). Proxies "suffer" from the same invariants than the regular objects which is that Object.getPrototypeOf cannot lie (there is no trap for it).
However, with proxies + redefining Object.getPrototypeOf + adding a custom Object.changePrototype(object, newProto) (which would /virtually/ change the prototype of a proxy (not a regular object!)), it could be done.
Le 24/09/2011 23:37, Jake Verbaten a écrit :
On Sat, Sep 24, 2011 at 5:03 PM, David Bruant <david.bruant at labri.fr <mailto:david.bruant at labri.fr>> wrote:
I'm a big fan of the proto operator proposal, however, as raised previously this operator relies on the object being created to have an intialisation syntax. This prevents, for instance, Date objects to have a custom prototype with this method. I am not very familiar with it yet, but I think that if they ever came to life, ParallelArrays [2] would suffer from the same problem. By the way, could someone add this concern as a note ("open issue" or "limitation" or something like this) in the proto operator page, please?
I don't understand the limitation for
Date
.var d = proto <| new Date();
Based on the proposed grammar (see harmony:proto_operator#overview ), it would fail since "new Date()" isn't a ProtoLiteral. JavaScript has a very dynamic nature:
this.Date = function(){console.log("Date")}; console.log(new Date()); // Logs 'Date' and create a new object as in {};
"Date" is not an element of syntax, it's something with a value decided at runtime. When you call "new Date()", you can't know in advance what is going to happen, so, you have to call it, but when it's called, the object is here already with a given prototype, so you can't change it.
It's different when you have syntax:
var o = {}; var a2 = o <| [1, 2, 3];
Here, the array syntax literal guarantees (since ES5, not in ES3!) that a native array is going to be built. Same goes for "function(){}" or any object created with reliable syntax, so the parser can both safely build the object and assign the chosen prototype.
That's my interpretation though and i'd be happy to hear from others.
On Sep 24, 2011, at 9:03 AM, David Bruant wrote:
... I'm a big fan of the proto operator proposal, however, as raised previously this operator relies on the object being created to have an intialisation syntax. This prevents, for instance, Date objects to have a custom prototype with this method. I am not very familiar with it yet, but I think that if they ever came to life, ParallelArrays [2] would suffer from the same problem. By the way, could someone add this concern as a note ("open issue" or "limitation" or something like this) in the proto operator page, please?
The solution for Date that I intend to implement for ES.next is to respecify Date so it uses a private named property instead of the [[PrimitiveValue]] internal property to store the time value. The primary thing this changes is that private names properties are inheritable. That means that you will be able to subclass date like so:
let ExtendedDate = Date <| function (...args) { super.constructor(...args); /*this will initialize the private time value property / / any ExtendedDate constructor initialization */ }
It will probably makes sense to redefine all the private state internal properties in a similar manner and hence make them all "subclassable" . With private names available to implement private instance variables the only things that really need to be non-inheritable "internal properties" are the semantic methods such as [[Get]], [[Put]], [[DefineOwnProperty]].etc.
It's different when you have syntax:
var o = {}; var a2 = o <| [1, 2, 3];
Here, the array syntax literal guarantees (since ES5, not in ES3!) that a native array is going to be built. Same goes for "function(){}" or any object created with reliable syntax, so the parser can both safely build the object and assign the chosen prototype.
That's my interpretation though and i'd be happy to hear from others.
That would seem to call for <| to be a copying operator:
- if RHS is a literal, original object can still be safely updated in place
- if RHS is a non-literal object, a copy of that object is created and updated
According to the first point in the proposal comments:
harmony:proto_operator#commentary_and_rationales
the definition of cloning/copying is the show-stopper.
Not sure how that interacts with the RHS function literal special case. I assume that if we had an accepted short function syntax, that special case would not even exist. Instead, we'd use:
function(..args) { return (proto <| functionLiteral(..args) ); }
But I'm probably misunderstanding that special case anyway..
Claus
For the record, function proxies could be used to implement a work-around.
There's a strawman to extend function proxies with support for custom prototypes: < strawman:function_proxy_prototype>. I'm
not proposing this as a mechanism for function subclassing, simply as a potential work-around until we have something better (such as the <| operator).
In any case, this work-around would work only if the function's prototype does eventually inherit from |Function.prototype|.
Cheers, Tom
2011/9/24 Jake Verbaten <raynos2 at gmail.com>
Le 24/09/2011 23:50, David Bruant a écrit :
Le 24/09/2011 22:25, François REMY a écrit :
I really think Proxies provides all the functionnalities required to implement the special border cases where a non-conventionnal behavior is needed. I do not think they do on their own, especially when it comes to object identity (equality of objects after a call to Object.getPrototypeOf). Proxies "suffer" from the same invariants than the regular objects which is that Object.getPrototypeOf cannot lie (there is no trap for it).
However, with proxies + redefining Object.getPrototypeOf + adding a custom Object.changePrototype(object, newProto) (which would /virtually/ change the prototype of a proxy (not a regular object!)), it could be done.
Hi,
I just wanted to give a follow-up on that. I implemented it: DavidBruant/HarmonyProxyLab/tree/master/MutableProtoObjects
var o = MutableProtoObject({a:1, b:2}); console.log(o.a, o.b); // 1, 2 (inherited)
o.b = 3; console.log(o.a, o.b); // 1 (inherited), 3 (own)
Object.changePrototype(o, {a:2, b:4, c:1}); console.log(o.a, o.b, o.c); // 2, 3 (own), 1
delete o.b; console.log(o.a, o.b, o.c); // 2, 4, 1 // shadowing of b is over.
-
MutableProtoObject is an equivalent to Object.create (without the second argument).
-
Object.changePrototype(o, newProto) works as you'd expect.
-
Only newly created objects with MutableProtoObject can have their prototype changed dynamically (while proto can change built-ins)
-
There is no protection against prototype chain cycles (it wouldn't be hard to implement though).
Also, I implemented another flavor of Forwarder (similar to [1]) which explicitly calls Object.getPrototypeOf when needed (which allows me to return a different value when needed). I have no opinion on whether it's better or worse, but i just wanted to point it out.
David
2011/9/27 David Bruant <david.bruant at labri.fr>
I just wanted to give a follow-up on that. I implemented it:
Nice experiment.
Also, I implemented another flavor of Forwarder (similar to [1]) which
explicitly calls Object.getPrototypeOf when needed (which allows me to return a different value when needed). I have no opinion on whether it's better or worse, but i just wanted to point it out.
Right, I've also been thinking about this. There are two possible forwarding handlers:
- one that uses the "virtual" prototype passed to Proxy.create
- one that uses Object.getPrototypeOf(target)
Option #2 is what's in the proposal [1]. I think that's a safe bet, since most proxies will not want to interfere with the prototype chain and will probably even set the virtual prototype equal to the target's prototype, in which case #1 and #2 are the same. We could make the choice between #1 and .#2 configurable, but I don't think it's worth the additional complexity.
What's the current status on proposals for Function.create?
At present the inability to set a prototype on a Function instance is a real hindrance to creating objects that are both new()-able and have properties. The same goes for creating callable objects that happen to have properties or methods.
The two existing workarounds - proto and proxies - are apparently a serious hindrance to optimization capabilities and the latter also increases code complexity considerably.
I have heard hints that at some point it might be possible for an ES6 class to inherit from Array or Function, which implies being able to create a Function with a custom prototype. Is this planned to be the case? I'm told that neither V8 or SM support this at present.
I've been researching this because the inability to create a Function instance with a prototype is one of the largest handicaps that remains for JSIL. I think the same will be true for any compiler that tries to interop with native JavaScript while providing more robust type system semantics. Not only does it produce an enormous performance penalty, but it complicates the runtime code considerably.
The proto workaround would involve creating the prototype for the function first (as a stand-alone object), then setting it onto the Function instance after creating it. Sadly my research suggests that this is a full, permanent deopt of the Function instance and that it is not likely that this will change anytime soon.
The issues with using a proxy to enable this should be self-evident; if people have a strong suspicion that this will become fast in the near future, that would be great to hear. From a design perspective using a proxy to solve this seems like using an enormous hammer to lightly tap on a bent nail.
A summary of my particular scenario: I have a cyclic dependency problem when initializing object graphs that contain Functions. For Object, it is possible to break these cycles by first constructing the prototype (and passing it around/storing it) in order to temporarily break the cycle, before you finally attach the fully-initialized prototype to its destination object. This is not possible with Function, because you must construct your function body before setting properties onto the Function instance. This makes the construction of various cyclic object hierarchies impossible. The 'temporary prototype' solution becomes difficult because in this scenario, the properties literally have to be transplanted (manually, via assignment) from the old object onto the new, which means that any retained references to the 'placeholder' object will become out of sync with the new object. Using the placeholder object as the prototype of the new Function allows these two to remain in sync, and effectively be identical (other than the ability to new() them).
The .NET type system is a good example of type hierarchies that form an interconnected graph. Representing these type objects as callables (to enable 'new') means we have to be able to create a type's constructor before we have created and defined the type, and this means that the only way to define that constructor is for it to be a delegating constructor that calls .apply() on a function reference from a closure that is initialized later. Thus far it has been impossible for any JS runtime to optimize these delegating constructors (I've tried lots of variations), and they seem to also prevent the creation of densely packed, optimized shapes for instances. The result is that this pattern produces increased memory usage and reduced performance.
Function.create would enable considerably simpler JS for this scenario and similar scenarios. I suspect other languages that try to integrate natively with JS (vs creating your own isolated bubble like emscripten/asm.js) will face similar issues. The other compilers I've seen that tackle this solution efficiently do so by abandoning the 'new' keyword in favor of magic methods, which I consider a poor solution.
Thanks,
If you intend to have a constructor that inherit from another one, it is possible in ES6: just recall that, in ES, constructors and classes happen to be the same thing:
class C extends D { // alternatively: let C = class extends D {
constructor(...) { ... }
}
Using the extends D
clause is more or less equivalent to execute the following instructions:
Object.setPrototypeOf(C, D)
Object.setPrototypeOf(C.prototype, D.prototype)
with the restriction that D
must be a constructor itself. If I understand correctly your use case, D
would just be a placeholder constructor. I can't speak about performance, however.
—Claude
Le 16 juin 2014 à 09:06, Katelyn Gadd <kg at luminance.org> a écrit :
On Mon, Jun 16, 2014 at 3:06 AM, Katelyn Gadd <kg at luminance.org> wrote:
emscripten/asm.js) will face similar issues. The other compilers I've seen that tackle this solution efficiently do so by abandoning the 'new' keyword in favor of magic methods, which I consider a poor solution.
I'd be interested in hearing more about why you feel this is a poor solution. It seems to me that two part construction would work fine -- that is, don't let the code inside the Function object access the prototype until the appropriate properties have been set, so you don't have a sync issue. --scott
ps. alternatively, it seems like you could use proxies to attack the sync issue directly.
Claude: Thanks, I was under the impression that existing ES6 class inheritance wouldn't allow this to work. If it does, that's a big improvement and I'll have to try it once class syntax is working in one of the browsers I target.
Scott: The problem isn't a 'sync issue'; the body of the function isn't accessing the prototype, the body of the function depends on the prototype. For example, the number of arguments the function takes depends on how many arguments the type's actual constructor has; I can't know that until I've enumerated all the methods of the type (one of the operations I have to do when building the type graph). A method has a list of argument types and a return type; those types are, naturally, type objects. It's possible for a type to have a method that accepts its own type as an argument, so at that point you have a very trivial cycle. This is why I end up having to create highly inefficient delegating constructors in this scenario.
I solve a lot of the trivial cycles by basically doing my own manually-collected weak references, where I assign types unique identifiers and store those inside of reference tokens and resolve them at runtime. This solves the simpler cases (at the cost of destroying my performance and bloating the heap). However, there are nastier cycles that can't be broken this way. I currently have something more like a 'four part construction' algorithm and it is still not enough to break all these cycles. The introduction of manual weak references, manually auditing code to ensure references don't escape when they shouldn't, and manually arranging things so that type initialization occurs in a sensible order is all a huge pain and produces fragile code. Being able to create a Function instance with a prototype solves almost all of this. :)
The 'create a placeholder object and pass it around until the real object is ready' solution could work right now in a language with better auditing tools; the problem there is purely that I have no way to enforce that the placeholder object doesn't escape into the heap. Once it escapes, everything is ruined because I now have two versions of type T floating around, and various code will be doing gets and sets onto its version of T. I don't have any way to scan the heap and replace all references to the old object with the new object, or make the old object magically replace itself with the new object. (I suppose I could sort of do this by making the old object a proxy?)
There is no standardized way to create a new function with a prototype which is not Function.prototype.
I propose Function.create
/* * Creates a new function whose prototype is proto. * The function body is the same as the function fbody. * The hash of propertydescriptors props is passed to defineproperties just like * Object.create does. */ Function.create = (function() { var functionBody = function _getFunctionBody(f) { return f.toString().replace(/.+\{/, "").replace(/\}$/, ""); }; var letters = "abcdefghijklmnopqrstuvwxyz".split(""); return function _create(proto, fbody, props) { var parameters = letters.slice(0, fbody.length); parameters.push(functionBody(fbody)); var f = Function.apply(this, parameters); f.__proto__ = proto; Object.defineProperties(f, props); return f; }; })();
This is the same as Object.create except the second parameter is a function.
It will create a new function whose function body is the same as the function passed in.
I don't believe this is possible to do in ES5 without
__proto__
jsfiddle showing an example. jsfiddle.net/8CrqR
Related stackoverflow question stackoverflow.com/questions/7539148/how-do-i-inherit-functions