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

Reply via email to