-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mixin issues #29
Comments
Despite appearances the protocols proposed aren't using strings by default for example this: protocol Fizz {
buzz;
}
class Foo implements Fizz {
buzz() {
}
} Is not a valid implementation of the protocol class Foo implements Fizz {
[Fizz.buzz]() {
}
}
new Foo().buzz // undefined
new Foo()[Fizz.buzz] // [Function: Fizz.buzz]
Fizz.buzz // Symbol("Fizz.buzz") With this most of your points don't apply (unless using the string based version which is there for backwards compatibility with existing protocols not necessarily for general use):
Now I don't really have suggestions for the gorilla-banana problem other than to just not worry about it, if your protocols have lots of fields then this might be a design flaw with the protocol but if it isn't then there's surely a reason the protocol requires those fields? Something that is probably needed is optional fields when not everything is required to be implemented e.g. maybe something like (not sure if this is what you were getting at): // Supposing Iterator was implemented using the proposed protocol
protocol Iterator {
next;
return?; // Still creates Iterator.return
// but doesn't require implementation
throws?; // Ditto
}
class ArrayIterator implements Iterator {
[Iterator.next]() { /* Not optional */ }
[Iterator.return]() { /* Optional, could be emitted */ }
} And regarding the private state thing, I raised a point about this a while ago, however I don't think it's super critical at least in the initial designs of this proposal given that it's generally bad practice to poke and mess with arbitrary symbol properties on objects. |
In fact, the same symbol can be used in different protocols. See this issue
What do you mean? You can not do that if you inherit the implementations. You have to look in the protocols themselves.
That is only if you implement the actual method yourself. All these, together with sharing private state, are probably not important issues for protocols since probably protocols will mostly not be used as mixins, although they could easily be abused like that. My point was that since they can work as mixins, it would be better to have a more capable and better designed mixin mechanism and use that for protocols as well. |
Yes but this is ultimately a design decision, reusing a Symbol (inside a protocol or not) risks collision. I'd have to see a concrete example of the problem in #20 to see that you'd ever want to do this in practice in a way that would cause collisions.
Well I mean you know that it inherits everything that the protocol defines, by definition you already have access to the protocol to implement it so given that fact you can simply use whatever is available. There's nothing different from subclassing here, you might need to inspect the chain upwards to discover all things a class has I don't see anyway around this e.g.: const bar = Symbol('bar')
class A {
foo() {
}
[bar]() {}
}
protocol B {
fizz() {
return 12
}
}
// For both A and B you simply have to inspect their definition to know
// what you're getting, there's nothing special about protocol in this
// regard, you simply have to inspect the rest of the chain
class C extends A implements B {}
I don't understand what you mean, pre-defined ones will simply exist on the thing that implements the protocol: protocol Mixin {
mixinMethod() {
return 10;
}
}
class Foo implements Mixin {}
new Foo()[Mixin.mixinMethod]() // 10
if (someArbitraryValue implements Mixin) {
// If something implements a protocol it gets
// pre-defined methods for free
someArbitraryValue[Mixin.mixinMethod]() // 10
} |
I was just saying you can get collisions, I'm not saying it's ok to share the symbols.
Sure, inheritance has this issue as well. In fact, this kind of mixins are a sort of multiple inheritance. Again, my point is that if a generic well designed mixin mechanism is added later (and I wish it was sooner), it would be better for the protocols to use it instead of having yet another kind of inheritance. |
@raulsebastianmihaila What is it that you would like out of a "mixin" feature that you don't feel protocols provide? I would like for this proposal to satisfy the community's desires for mixins. The way I understand it, mixins are simply protocols with no required symbols. All of the points in your original comment have been addressed, and the "gorilla-banana problem" you're referring to doesn't seem to cause an actual issue: there's no downside to having additional symbol-named properties. |
@michaelficarra In essence I want mixins to be based on composition and to allow sharing private state with the mixin functions. My understanding is that the protocols are some sort of contracts, which I think is a good idea. Mixins, on the other hand, are for reusing code. These are two different concerns. I don't see code reuse as the primary purpose of protocols. Also the primary purpose (if at all) of mixins is not to impose a contract. I'm not certain I understand what you mean by 'the points have been addressed' as it can be intepreted in two different ways (they're either acknowledged or they're solved). I certainly don't think the issues are solved. If protocols are only used for implementing contracts, then I don't consider these issues to be critical and because of this I can consider them to have been addressed (in both ways). These issues, however, are very important for mixins (if protocols are used as a general mixin mechanism).
All these 4 issues are solved in a composition based mixin mechanism by explicitly picking every item that you need from the mixin providers. The last (5th) issue with protocols is the inability to share private state. This might not be important or there may be ways around this for contracts. But since mixins are ways of reusing partial definitions of system concepts, while these concepts will use private state, it's important to be able to use the private state in these definitions. Of course, since private state shouldn't be accessible for everybody, the private state needs to be explicitly introduced in trustworthy mixin providers from the context where the mixins are mixed in. To address (solve) all these issues, I came up with a composition based mixins proposal that allows sharing private state. I call these mixins definition mixins. The syntax looks as follows: function mixin1(obj, mix, privObj) {
return (v) => obj.q + mix.mixin2(3, privObj.m);
}
function mixin2(obj) {
return (x, y) => x + y + obj.z;
}
class Destination {
#privObj = {m: 100};
q = 3;
z = 1000;
mixin this:
mixin1,
mixin2:
this.#privObj;
method() {
this.#mixin1(35);
}
} More details about the syntax/semantics can be found in the github proposal or in this esdiscuss thread. An important detail is that this mechanism also works in a function context, where constant bindings are created and where closure based private state can be used. The mixins can also be public methods of the context object. Please let me know if anything regarding my proposal is not clear. I think it's important to be clear about whether the protocols mechanism is meant to be used as a general purpose mixin mechanism and not only for contracts. If they are a general mixin mechanism, it has all these disadvantages in comparison to my proposal. If it's mainly for contracts and it also uses a mixin mechanism in order to provide default implementations, then 1) we should have a separate more capable and well designed mixin mechanism that can be used outside protocols and 2) it would make more sense for the protocols to use the same mixin mechanism, instead of using a different mixin mechanism. I also worry that if there is no separate mixin mechanism the protocols will be abused as mixins and as I showed, it would not be fun. Definition mixins could be used together with protocols. The protocols could then be used to impose a contract, while the mixin mechanism can be used to pick the default implementations from the protocols. protocol P {
a() { /* ... */ }
}
class C implements P {
mixin on this: P.a;
} This way the protocols could also use private state. |
I missed the fact that protocols can not provide default implementations for properties with string keys. Sorry for that. That means that property collisions are indeed very unlikely to happen with protocols. However this also makes them less feasible as a general mixin mechanism, as symbols are more difficult to work with than strings. |
Without the ability to implement string keys, protocols cannot serve the purpose of general mixins. The detractions to ergonomics, discoverability, and transparency implied by Symbol keys will make using protocols as mixins DOA. However, from reading the recent meeting notes it seems that protocols are intended to subsume mixins. |
@aluanhaddad How would you expect a mixin system based on string-named properties to handle collisions? |
I think that the fact that protocols are also a kind of mixins is problematic. First, because they are a naive kind of mixins and, secondly, because I think it would be better to separate the two mechanisms. These are the issues with this kind of mixins:
I have a proposal for a different kind of mixins that don't have all these issues. They also work with both function syntax and class syntax and I believe they can be made to work with protocols. This way the protocol and mixin mechanisms can stay separate.
Please see here a more detailed ongoing discussion.
The text was updated successfully, but these errors were encountered: