Andrea, > losing getters and setters where super is also allowed is all we *don't* need as well, not sure why keep ignoring the fact Object.assign has undesired side-effects.
Right, but that's well-explained (for example, in the MDN docs). It is also easy to use `getOwnPropertyNames` or `getOwnPropertyDescriptors`. But, even if we copy descriptors, the static nature of `super` *will still get in the way* when copying a getter/setter descriptor from one object to another. So, my argument is the same: a dynamic `super` makes `Object.assign` *as well as any other methods of method borrowing* work as expected (talking strictly in context of how `super` would work, ignoring other side-effects not relating to `super`). - Joe */#!/*JoePea On Mon, Aug 1, 2016 at 1:06 AM, Andrea Giammarchi < andrea.giammar...@gmail.com> wrote: > > I'm just re-iterating, but `Object.assign()` is all we need > > losing getters and setters where super is also allowed is all we *don't* > need as well, not sure why keep ignoring the fact Object.assign has > undesired side-effects. > > On Mon, Aug 1, 2016 at 8:30 AM, Michał Wadas <michalwa...@gmail.com> > wrote: > >> fn.bind().bind() can't throw because of backward compatibility. >> >> On 1 Aug 2016 9:21 a.m., "/#!/JoePea" <j...@trusktr.io> wrote: >> >>> Allen, I read your linked documents. After thinking about it more, I >>> believe it would be ideal to have both: >>> >>> 1. a tool like `toMethod`. I like "setHome" better as that describes >>> much more concisely what is happening. `.bind()` is already a way to make a >>> function *behave as a method* of the object passed to `.bind()`, so >>> `toMethod` clashes with that. Also, we're not really assigning a method >>> onto an object with `toMethod`, so it seems awkward. We're setting the >>> HomeObject, that's what we're doing. >>> 2. and a dynamic `super`. >>> >>> > `func.bind().toMethod(...)` should throw >>> >>> I believe that `.bind` and `.toMethod` are useful not just for keeping >>> contexts, but so that when passing a function away from our own processes >>> to external processes those external processes cannot tinker with `this` >>> and `super`. The external processes may store the functions inside objects, >>> and we may deem it important that the passed functions act upon the context >>> we specify instead of those storage containers. This is one thing arrow >>> functions solve regarding `this` and `super` (more on that below). But, if >>> "`func.bind().toMethod(...)` should throw", then how does one bind both >>> `this` and `HomeObject` in order to guarantee that some external process >>> doesn't modify one or the other (or for some other reason)? >>> >>> Thus, if `let func = function() {}`, it might make sense for the >>> following to be true: >>> >>> - `func.bind(...)` does not throw >>> - `func. >>> toMethod >>> (...)` does not throw >>> - `func.bind(...).toMethod(...)` does not throw >>> - `func. >>> toMethod >>> (...). >>> bind >>> (...)` does not throw >>> >>> and errors should be thrown only when `bind` or `toMethod` are called >>> on something that is already `bind`ed or `toMethod`ed, respectively: >>> >>> >>> - `func.bind(...). >>> bind >>> (...)` >>> throws >>> - `func. >>> toMethod >>> (...) >>> .toMethod >>> >>> (...)` throws >>> - `func.toMethod(...). >>> bind >>> (...) >>> .bind(...) >>> ` throws >>> - `func.toMethod(...).bind(...). >>> toMethod >>> (...)` throws >>> - `func. >>> bind >>> (...). >>> toMethod >>> (...). >>> toMethod >>> (...)` throws >>> - `func. >>> bind >>> (...). >>> toMethod >>> (...). >>> bind >>> (...)` throws >>> >>> It would also be important to consider that passing >>> `this.func.bind(...)` to an external process still leaves `super` up for >>> modification, and vice versa if passing `this.func.toMethod(...)`. >>> >>> If someone has done `this.func.bind()` and has no idea what to pass to a >>> following `.toMethod` call in order to prevent external processes from >>> tinkering with it (which will be the case most of the time because >>> requiring the user to lookup where on the prototype chain a method is >>> located is requiring way too much work of them in order to achieve a simple >>> outcome), then maybe there can be a new `.lock()` method that simply >>> returns a new function `bind()`ed and `toMethod()`ed to whatever `this` and >>> `super` are relative to the object passed to `lock`. For example, >>> >>> ```js >>> $('.some-el').on('click', this.func.lock(this)) // binds both `this` and >>> `HomeObject` >>> ``` >>> >>> would be essentially equivalent to >>> >>> ```js >>> $('.some-el').on('click', this.func.bind(this).toMethod( findHome(this, >>> 'func', this.func) )) >>> ``` >>> >>> where `findHome` would be a necessary helper to have laying around, >>> which proves that `toMethod` is not ideal for average cases, only for >>> people involved in meta-programming. >>> >>> The "issues with toMethod" in your [mixin-proposal.pdf]( >>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mixin-proposal.pdf) >>> seem like implementation details that don't need to surface to the end user >>> of the language. For example, consider the issues of `toMethod` >>> >>> > - Requir[ing] copying >>> > - Hav[ing] the "deep clone" problem >>> >>> I don't think copying or cloning is necessary. Internally, the engine >>> could use a proxy that simply passes to the wrapped internal function >>> `this`, `HomeObject`, or both, depending on which are bound, otherwise the >>> engine falls back to contexts based on `this` and where in the prototype >>> chain from `this` that `super` is being used (that algo doesn't seem >>> expensive since the LookUp algo already finds the prototype (HomeObject) >>> where a property lives, assuming `super` to be dynamic). >>> >>> Internally, the proxy returned from `func.bind(...)` would contain the >>> reference to the `this` argument, and leave `super` to the lookup algo >>> starting lookup at that `this` context. Internally, the proxy returned from >>> `func.toMethod(...)` would contain the `HomeObject` reference to pass into >>> function calls of the wrapped function. The things returned are proxies, so >>> property lookup could be forwarded without having to worry about cloning. >>> We could call `.toMethod()` or `.bind()` on either of those two proxies, >>> respectively, in which case the proxies record the second `HomeObject` or >>> `this` arguments, respectively. Any more calls and an error can be thrown. >>> >>> Implemented along those lines, there doesn't need to be any copying or >>> cloning. (Maybe I'm missing why that is required, but it doesn't seem to be >>> required the way I'm imagining it). >>> >>> To make `.bind()` and `.toMethod()` *not* seem like magic (because >>> currently `.bind()` returns a function who's direct prototype is >>> `Function.prototype`, which is somewhat like magic and makes it foggy how >>> the new function is related to the old one), there could be a new >>> `FunctionProxy` class (it might extend from `Function`, or `Proxy` *and* >>> `Function` if there were multiple inheritance). It would not be possible to >>> get a reference to the original function from the `FunctionProxy` so that >>> unauthorized manipulation of `this` and `super` can be guaranteed when that >>> is what is wanted. >>> >>> So, the following snippets would be effectively the same: >>> >>> ```js >>> let f = function() {console.log(this, super)} >>> f = new FunctionProxy() >>> f.bind(this) >>> f.toMethod(findHome(this, 'nameOfMethodWeAreIn', >>> this.nameOfMethodWeAreIn)) >>> ``` >>> >>> ```js >>> let f = function() {console.log(this, super)} >>> f = f.bind(this) // returns a FunctionProxy >>> f = f.toMethod(findHome(this, 'nameOfMethodWeAreIn', >>> this.nameOfMethodWeAreIn)) // returns the *same* FunctionProxy. >>> ``` >>> >>> ```js >>> let f = function() {console.log(this, super)} >>> f = f.bind(this).toMethod(findHome(this, 'nameOfMethodWeAreIn', >>> this.nameOfMethodWeAreIn)) >>> ``` >>> >>> ```js >>> let f = function() {console.log(this, super)} >>> f = f.lock(this) // The lock method finds the home of `f` relative to >>> `this` if any, otherwise sets HomeObject to `this`, or similar. >>> ``` >>> >>> The combination of `bind` with `toMethod` (or a new `lock` method) in >>> those examples shows how to achieve essentially the same as with arrow >>> functions, so >>> >>> ```js >>> let f = function() {console.log(this, >>> super)}.bind(this).toMethod(findHome(this, 'nameOfMethodWeAreIn', >>> this.nameOfMethodWeAreIn)) >>> ``` >>> >>> is effectively equivalent to the following using arrow functions >>> (disregarding `arguments` and `new.target`): >>> >>> ```js >>> let f = () => {console.log(this, super)} // <-- yes! >>> ``` >>> >>> The [mixin-proposal.pdf]( >>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mixin-proposal.pdf) >>> mentions that >>> >>> > Programmers don't need to think about or even be aware of the >>> internals of super. >>> >>> Programmers are currently aware of needing to sometimes bind functions >>> passed to other libraries, or to bind functions in order to make them >>> operate on certain objects as if they were methods of those objects, etc. >>> >>> Well, considering developers know this about `.bind()`, the nice thing >>> about passing `obj.func.bind(obj)` to an external process is that the >>> external process can call the passed function and that call will be >>> equivalent to calling `obj.func()` in the original process, >>> *and therefore a dynamic `super` will still work as expected without >>> programmers having to actually use `toMethod`* >>> because lookup for `super` would start at the prototype chain leaf which >>> is referenced by the `this` that was bound. This concept is backwards >>> compatible with pre-ES6 `.bind()` usage, and so people coming from ES5 and >>> using `.bind()` in ES8 or ES9 (or wherever dynamic `super` lands) will not >>> be surprised, and it will be intuitive. >>> >>> Note that passing the bound function in the last paragraph still leaves >>> the function susceptible to being called with a tinkered `HomeObject`. >>> >>> `toMethod` will be mostly useful for interesting cases where people are >>> implementing frameworks or tools and need to have control over classes or >>> inheritance schemes are implemented, or something meta-programming like >>> that. But, people simply defining classes will never need to worry about >>> `super` internals even if it is dynamic and if `toMethod` exists. >>> >>> A dynamic `super` means that `super` would be allowed in any function >>> definition, not just object-initializer methods or class definition >>> methods, which makes it easy to `Object.assign()` things, copy methods >>> ad-hoc, and basically enjoy ES5-like techniques. ES6 classes and >>> object-initializers should be limited to being syntactical shortcuts as >>> much as possible, and not impose limitations on developers that cause >>> developers to have to abandon pre-ES6 techniques (copying methods from one >>> prototype to another for example) just because something like `super` is >>> unfortunately static. >>> >>> ### This destroys the pre-ES6 flexibility that developers had in >>> manipulating prototypes because now they cannot apply the same techniques >>> with ES6 classes. >>> >>> It may be tempting for someone to refute this and say "well, then just >>> write your classes the ES5 way". That's fine and dandy, but then it means >>> my tools for manipulating ES5 classes will be highly incompatible with ES6 >>> classes (which are supposed to be "syntax sugar"). >>> >>> This is bad because it causes fragmentation in the ecosystem. Some >>> libraries will work only with ES5 classes, others with ES6 classes, and >>> some libraries will be incompatible with each other because the paradigm >>> has forked into two directions rather than ES6 being a pure extension of >>> ES5. >>> >>> ### If super is static, then should I care about prototypes anymore? >>> Maybe I shouldn't care about them anymore because they can't be manipulated >>> without `super` getting in the way. Should I stop thinking about JavaScript >>> as a prototypal language and abandon my efforts at manipulating prototypes >>> in order to create a multiple-inheritance scheme? Should I just write ES6 >>> classes and not think about prototypes and pretend JavaScript was never a >>> prototypal language? >>> >>> That is not what I want, and I bet highly that many of us here don't >>> want that, but that is the direction that a static `super` is taking us, >>> and the `mixin {}` operator idea is like a rocket propelled explosive going >>> down that wrong path in the paradigm fork that I mentioned just moments >>> ago, ready to cause fragmentation, as in "oh, I'll use `Object.assign` on >>> my ES5 classes, and `mixin {}` on my ES6 classes" or "darn, I wanted to use >>> ES6 classes because I like the syntax, but I can't because static `super` >>> is in the way. So much for syntax sugar.". (plus, ES6 constructors not >>> being callable is also in the way in my case, as I can't simply call them >>> on another `this` which I really want to do, but I'll save that for another >>> day, and will just use my [`Class().extends()` API]( >>> https://www.npmjs.com/package/lowclass) to mimic ES6 syntax in defining >>> all my classes, but then I will actually be able to implement my >>> `multiple()`-inheritance helper without needing `eval` and unfortunately >>> without using ES6 classes -- maybe I'll use ES9 or ES10 classes when >>> `super` is dynamic...) >>> >>> There's the current path (static `super`) and the path that is in-line >>> with ES5 (dynamic `super` and ES5-based tools like `Object.assign` behaving >>> intuitively). >>> >>> (Note, some may want a function mixed into somewhere to have it's >>> original HomeObject, and that is what `toMethod` can be useful for. >>> Everyone can be satisfied.) >>> >>> The world will be much better this way, people will have more freedom to >>> implement interesting things, and it will be more intuitive (`_.extend` >>> will keep working in cases where methods use `super`!); it's better for the >>> world to have this flexibility. Most developers will just use ES6 classes >>> without worrying about `super` internals, *even if* `toMethod` exists >>> and/or `super` is dynamic. `toMethod` usage will be rare, *but it will be >>> powerful when it is needed.* >>> >>> `toMethod` or a dynamic `super` will not be a footgun because most >>> people will never need to know or work with those details, plus with >>> `super` being dynamic it means JavaScript can behave the way we already >>> expect from two decades worth of using the keyword `this`, which I think is >>> A REALLY GOOD THING. >>> >>> It would mean that `this` and `super` can be used anywhere and we >>> wouldn't have to distinguish between functions and >>> methods-defined-by-object-initializer-shorthand (paradigm fragmentation). >>> The distinction is not intuitive. We can merge the paradigm fragmentation >>> by simply making `super` dynamic along with introducing the tool to >>> configure `HomeObject` on as-needed basis for the rare cases (and even if >>> it were a footgun, it's need is rare, and people who wield the footgun will >>> probably convert the footgun into a powerful tool of mass benefit). >>> >>> At the end of the day, it would be excellent for the following to work, >>> and is what would be intuitive, without requiring a new operator: >>> >>> ```js >>> let o = { >>> __proto__: other, >>> func() {super.func()} >>> } >>> ``` >>> >>> ```js >>> let o = { >>> __proto__: other, >>> func: function func() {super.func()} // this should literally be 100% >>> equivalent to the previous example >>> } >>> ``` >>> >>> ```js >>> let o = { >>> __proto__: other >>> } >>> Object.assign(o, { >>> func: function func() {super.func()} // and this should end up being >>> exactly equivalent too. >>> }) >>> ``` >>> >>> > Let's make mixing in methods as simple as `Object.assign(obj, {...})` >>> > Let's make it simpler with `mixin {...}` >>> >>> I'm just re-iterating, but `Object.assign()` is all we need, and new >>> operators are needed because they solidify the provably-unwanted and >>> unintuitive static `super`. Adding more to the language on top of static >>> `super` may cause more and more divergence between ES5 (things like >>> `Object.assign` -- I know it came out in ES6, but `_.extend` was out before >>> ES6) and ES6 (class-definition methods, object-initializer methods), and >>> fragment the language into two paths. A dynamic `super` solves the problem, >>> and it can be well documented so that developers will know what to expect >>> just like they already have with two decades of `this`. A dynamic `super` >>> is perfectly in line with the dynamic nature `this` (performance >>> considerations aside). >>> >>> If there really is a performance problem with a dynamic `super`, let's >>> just solve *that* instead of creating separate syntaxes that are >>> incompatible with the JavaScript we already know. A dynamic `super` is also >>> forwards and ahead of what `super` is in other languages. JavaScript >>> doesn't need to go in that direction, it needs to go furthermore forwards. >>> >>> Also keep in mind that a dynamic `super` will prevent surprises to >>> developers who are beginning to learn JavaScript in the post-ES6 era. >>> >>> We should strive for a dynamic `super` (which would be completely >>> backwards compatible with how it currently works) along with something like >>> `toMethod`. >>> >>> --- >>> >>> Does anyone mind pointing out the specific unwanted overhead costs of a >>> dynamic `super`? >>> >>> */#!/*JoePea >>> >>> _______________________________________________ >>> es-discuss mailing list >>> es-discuss@mozilla.org >>> https://mail.mozilla.org/listinfo/es-discuss >>> >>> >> _______________________________________________ >> es-discuss mailing list >> es-discuss@mozilla.org >> https://mail.mozilla.org/listinfo/es-discuss >> >> >
_______________________________________________ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss