Could you not make `multiple` be a function that returns a Proxy, rather than achieving multiple inheritance by copying properties?
On Wed, Jul 27, 2016 at 8:19 PM, /#!/JoePea <[email protected]> wrote: > For reference, here's the implementation of `multiple()` where multiple > constructors calls are possible, using a `callSuperConstructor` helper: > https://gist.github.com/trusktr/05b9c763ac70d7086fe3a08c2c4fb4bf > > */#!/*JoePea > > On Wed, Jul 27, 2016 at 8:16 PM, /#!/JoePea <[email protected]> wrote: > >> For now, I've settled with writing classes like this: >> >> ```js >> const SomeClassMixin = base => { >> class SomeClass extends base { >> // ... >> } >> >> return SomeClass >> } >> >> const SomeClass = SomeClassMixin(class{}) >> SomeClass.mixin = SomeClassMixin >> >> export {SomeClass as default} >> ``` >> >> This makes it possible for an end user to import the class and extend >> from it like normal: >> >> ```js >> import SomeClass from './SomeClass' >> >> class OtherClass extends SomeClass { >> // ... >> } >> ``` >> >> But, it also allows and end user to use it as a mixin: >> >> ```js >> import SomeClass from './SomeClass' >> import AnotherClass from './AnotherClass' >> >> class OtherClass extends SomeClass.mixin(AnotherClass) { >> // ... >> } >> ``` >> >> This has two main downsides: >> >> - The class creator has to write the class as a class-factory mixin, then >> remember to export the mixin application on a plain `class {}` >> - It is not possible to pass specific arguments to the constructors of >> each class. >> >> One of my implementations of `multiple()` *does* allow one to pass >> arguments to each constructor. I'll re-visit that when Proxy is supported >> in Safari. >> >> I can't think of any way to make this work using a class helper library, >> because those need to create prototype chains, and thus `super` cannot be >> modified unless using `eval`, in which case original scope is lost. >> >> I literally think that a dynamic `super` and/or >> `Function.prototype.toMethod` would allow me to implement the ideal >> solution, which would have the following benefits: >> >> I am going to leave this alone for now, and just continue with the >> class-factory mixin approach, although that is not my ideal solution. >> >> Any ideas or suggestions? >> >> */#!/*JoePea >> >> On Wed, Jul 27, 2016 at 7:51 PM, /#!/JoePea <[email protected]> wrote: >> >>> > 1) There are indeed use cases for dynamically configuring the >>> HomeObject binding but they are quite specialized >>> >>> I suppose my case is quite specialized. I am wanting to duplicate the >>> prototype chains of ES6 classes, but I have no reliable (afaik) way to make >>> the `super` keyword work on copied methods. If I copy the method by >>> reference then the `super` keyword is based on the wrong `HomeObject`. If I >>> copy the method by getting its the source with `.toString()` followed by >>> using `eval` to define the new method on an object initialization, that >>> works and `super` will work, but this has problems if the original method >>> relied on variables in the outer scope where it was originally defined as >>> the copied method will not have access to that scope (f.e. code transpiled >>> by Babel may not be able to see the Babel helper functions). >>> >>> I am trying to implement a function `multiple()` so that I can do >>> >>> ```js >>> class SomeClass { ... } >>> class >>> OtherClass >>> { ... } >>> class Foo extends multiple(SomeClass, OtherClass) { ... } >>> ``` >>> >>> and it's proving to be really difficult because I can't configure >>> `HomeObject`. The `multiple` function copies the prototype chains of each >>> class, then combines them together (when possible, otherwise an error is >>> thrown, depending on the native prototypes involved). As mentioned, this >>> somewhat works using `eval()` in the implementation, except that copied >>> methods don't work if they rely on variables of the outer scope where they >>> were originally defined. For example, suppose `SomeClass` in the >>> following example was imported from another file, and it depends on Babel >>> helpers defined in the original scope where it was imported from: >>> >>> ```js >>> import SomeClass from './somewhere/SomeClass' >>> class >>> OtherClass >>> { ... } >>> class Foo extends multiple(SomeClass, OtherClass) { ... } >>> ``` >>> >>> The `multiple()` call will copy the methods of `SomeClass` onto a new >>> prototype chain (the methods need new `HomeObjects`), but the new methods >>> will fail to reference anything from the original scope where `SomeClass` >>> came from. >>> >>> I need to be able to modify `HomeObject`s without using an `eval()` hack >>> in order for my `multiple()` implementation to work 100% as intended. >>> >>> For now I may need to abandon my effort and use class-factory "mixins" >>> as in >>> >>> ```js >>> let SomeMixin = baseClass => class SomeMixin extends baseClass { ... } >>> ``` >>> >>> , but the downside of this is that the classes are not usable as >>> standalone classes, so and end-user can't do >>> >>> ```js >>> class Foo extends SomeClass { ... } >>> ``` >>> >>> We are required to do >>> >>> ```js >>> class Foo extends SomeClass(null) { ... } >>> ``` >>> >>> but due to the `null` the class we won't have `Object` methods. We >>> could do >>> >>> ```js >>> class Foo extends SomeClass(Object) { ... } >>> ``` >>> >>> , but that is not also ideal as `Object` is not guaranteed to be the >>> native Object; `window.Object` could be overridden. >>> >>> I really want to define plain ES6 classes and use 3rd party plain ES6 >>> classes (not class factories). It means that those classes are not limited >>> to being used as mixins. >>> >>> For reference, here's the implementation so far (with the ugly caveats >>> of cloning methods with `eval`): >>> https://gist.github.com/trusktr/8c515f7bd7436e09a4baa7a63cd7cc37 >>> >>> I was planning to add caching so that combinations of classes could be >>> re-used (similar to what [mixwith.js]( >>> https://github.com/justinfagnani/mixwith.js) does to cache mixin >>> applications). >>> >>> > We aren’t going to do anything until there is significant real world >>> feedback saying that it really is needed. >>> >>> Here's that feedback. :D >>> >>> TLDR, it seems that without flexibility in modifying `[[HomeObject]]` in >>> order to control what `super` references, I cannot implement an ideal >>> `multiple()`-inheritance helper that I would be able to comfortably suggest >>> for use in a production app. I would definitely not recommend the my >>> `eval`-based implementation as it will fail in ways that are very >>> undesirable and unexpected. >>> >>> With the limitation of a static `super`, I can't do something simple >>> like use `Object.assign` to simply copy some methods from one class' >>> prototype onto the class where I need them. That's normal pre-ES6 stuff >>> that has worked along with a dynamic `this` since forever, so the change in >>> behavior from `this` to `super` doesn't fit with the pre-ES6 language that >>> we are previously accustomed to and that we love (I do). ES6+ is >>> backwards-incompatible with some pre-ES6 paradigms. >>> >>> Pleeeease, let's add something like `toMethod`, or make `super` dynamic >>> (preferably both). I don't see any downside to having something like >>> `Function.prototype.toMethod`, as it is an opt-in type of feature, so it >>> won't bring with it performance loss like a dynamic `super` might (I still >>> haven't heard conclusive evidence regarding those performance losses). >>> >>> From what I can imagine, property lookup checks checks the current >>> object, then goes to the next prototype and does the same, until finally it >>> reaches a prototype where the property is found. Once that object (a >>> HomeObject) is found, we know what the `HomeObject` is. It seems very easy >>> to call a found method with that found `HomeObject`argument, and when the >>> method is called and if it uses `super`, then a similar search will happen >>> up the prototype chain until the matching super property is found. The >>> second prototype search (the one initiated due to the method using `super`) >>> is already a requirement, so, this means that the extra overhead for a >>> dynamic `super` stems from simply passing as argument the `HomeObject` of a >>> method once the method is found on a prototype chain. A static super means >>> that the `[[HomeObject]]` variable is simply defined forever instead of >>> being marked in the property-lookup of a method, and that doesn't seem >>> like tons of extra overhead. Note that the property lookup is going to >>> happen anyways, so why not mark the `HomeObject` during the property lookup >>> algorithm? >>> >>> *Is that really enough overhead to merit a static `super`?* If someone >>> can explain it (or link to it), that would be greatly appreciated. >>> >>> I also think having dynamic `super` (or at least a way to configure it) >>> opens up language possibilities that can make interesting things happen >>> (for example, my `multiple()`-inheritance implementation would work >>> smoothly and instanceof checks would also work thanks to `@@hasInstance`). >>> The mere fact that a dynamic or configurable `[[HomeObject]]` would allow >>> for the creation of a tool like what I am trying to implement is good >>> enough reason to have the feature. Who knows what other awesome ideas >>> people will come up with. >>> >>> */#!/*JoePea >>> >>> On Sun, Jul 24, 2016 at 7:54 PM, Allen Wirfs-Brock < >>> [email protected]> wrote: >>> >>>> >>>> On Jul 24, 2016, at 7:04 PM, /#!/JoePea <[email protected]> wrote: >>>> >>>> What if there was also something like `Function.prototype.bind` like >>>> `Function.prototype.with`, so `someFunc.with(homeObject)` returns a new >>>> function who's [[HomeObject]] is the specified `homeObject`. It would be >>>> possible to do `someFunc.with(...).bind(...)` to configure both the home >>>> object and `this`. >>>> >>>> >>>> This was included in the ES6 drafst for quite awhile. Initially with >>>> the name defineMethod and latter as toMethod. But it was eventually >>>> decided to remove it. The pros and cons of such a function were >>>> extensively discussed with in TC39 over a several year period. For example, >>>> see >>>> https://github.com/tc39/tc39-notes/blob/master/es6/2014-01/jan-28.md#more-on-tomethod >>>> >>>> >>>> The issues with defineMethod/toMethod are summarized in >>>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mixin-proposal.pdf >>>> and https://github.com/allenwb/ESideas/blob/master/dcltomethod.md which >>>> is a proposal for an alternative way to address the problem. Notes from >>>> that discussion are at >>>> https://github.com/tc39/tc39-notes/blob/master/es6/2015-03/mar-25.md#6iv-a-declarative-alternative-to-tomethod-allen-wirfs-brock >>>> >>>> >>>> Where it has been left by TC30 is roughly: >>>> 1) There are indeed use cases for dynamically configuring the >>>> HomeObject binding but they are quite specialized. >>>> 2) All of the solution that have been proposed are confusing or error >>>> prone. >>>> 3) So far, the actual user demand for such a feature is small. >>>> 4) We aren’t going to do anything until there is significant real world >>>> feedback saying that it really is needed. >>>> >>>> Allen >>>> >>> >>> >> > > _______________________________________________ > es-discuss mailing list > [email protected] > https://mail.mozilla.org/listinfo/es-discuss > >
_______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

