Conversation
Also removed an unnecessary check that would have already been handled by the parser.
|
(Commenting just on the PR description so far.) Leads-question issue #1000 is also relevant. Looks like you are taking the "data fields" approach, which I think is aligned with the thinking at the beginning of the year. Looking through the past discussions, I'm wondering what position is being taken on these design questions (as a plan):
My main concern is that it looks to me like some changes are needed to the current approach to support things we expect to need. Right now it looks like all members are injected, which is going to make it hard to support the intrusive list use case (and evolution). And the lack of name when including a mixin in a type is going to make adding data members tricky unless you have some other plan. There are other use cases I expect we need to address, like injecting implementations of virtual methods defined in a base of the containing class, mixins extending base types, and so on, but that looks independent of what is in the current PR. The "Duality of |
josh11b
left a comment
There was a problem hiding this comment.
I've only reviewed the test cases, not the code.
| __mix M2; | ||
| __mix M3; | ||
| } | ||
|
|
There was a problem hiding this comment.
Also:
__mixin M1 {
fn F1[me: Self](x: Self) -> Self {
return x;
}
}
__mixin M2 {
fn F2() {
}
__mix M1;
}
__mixin M3 {
__mix M1;
__mix M2;
}
Also:
__mixin M1 {
fn F1[me: Self](x: Self) -> Self {
return x;
}
}
__mixin M2 {
fn F2() {
}
__mix M1;
}
class C {
__mix M1;
__mix M2;
}
Are these constructs only a problem if they introduce name conflicts? What happens if the mixins have no members that conflict? Example:
__mixin M1 {
}
__mixin M2 {
__mix M1;
__mix M1;
}
Also:
__mixin M1 {
}
class C {
__mix M1;
__mix M1;
}
There was a problem hiding this comment.
Yes, all these examples would work because there's no name conflicts. We could add a more thorough check in the future. I'll try to address this in the proposal.
| return x; | ||
| } | ||
| __mix M1; | ||
| } |
There was a problem hiding this comment.
What if M1 doesn't have any members to conflict (or later they are all marked external or something)? Should this be an error or allowed? It is definitely a problem if it has any members that take space.
__mixin M1 {
__mix M1;
}
| fn F1[me: Self](x: Self) -> Self{ | ||
| return x; | ||
| } | ||
| __mix M1; |
There was a problem hiding this comment.
Isn't M1 incomplete at this point? We haven't seen the closing } of its definition yet. I guess that opens the question about whether you can mix a mixin that has only a forward declaration at the moment.
| // Here Self which is both the input and output type is a type variable | ||
| fn F[me: Self](x: Self) -> Self { |
There was a problem hiding this comment.
FYI, this is counter to my expectation for Self. I'd only expect this test to work with T of for T:! Type.
There was a problem hiding this comment.
We've had trouble understanding the purpose of for T. Perhaps you could explain?
What is your expectation for Self?
The meaning that we're currently using for Self inside a mixin is that it is a placeholder for the class type that the mixin will become a part of.
There was a problem hiding this comment.
My expectation for Self is that it represents the mixin object, and T would represent the containing type.
explorer/testdata/mixin/use-mixin-method-in-class-method.carbon
Outdated
Show resolved
Hide resolved
Hi Josh, These are all good questions. The main thing to realize is that the current PR just represents a baby step of exploration on mixins and does not represent any kind of commitment to particular design decisions. We're mainly figuring out the various mechanisms and algorithms that are needed in the type checker and interpreter. Regarding the data fields vs. base class approaches... the current PR is closer to what you call the base class approach, but like I said, that's not set in stone. Regarding name clashes... the current PR has zero support for resolving them, but of course we will need to address that. |
jsiek
left a comment
There was a problem hiding this comment.
Great first step Darshal. Let's keep exploring!
Co-authored-by: josh11b <josh11b@users.noreply.github.com>
Co-authored-by: josh11b <josh11b@users.noreply.github.com>
Overview
This PR contains an initial implementation of a mixin language feature inspired
from other programming languages like Swift. This implementation is used to
flesh out the details of the mixin proposal which will be coming out in the
future. The ideas behind this PR and the mixin proposal that we are working on
are based off of this document which summarizes the outcome of the open
discussions about mixins with some of the leads. The minutes of the mixin
related open discussions can be found here (in the January discussions) and here
(in the December discussions).
Class inheritance in OOP allows for both code reuse, and subtyping which in turn
allows polymorphism. Currently, interfaces allow for polymorphism while mixins
will be a way to allow code reuse in Carbon. Mixins will also be a way of having
multiple inheritance since we can mix multiple mixins into a class.
A basic example that demonstrates code reuse using mixins is shown below. The
example demonstrates how the
Operationsmixin’s member methodSquarecan bereused in both the
PointandComplexclasses using the mix declaration whichis a member of both classes.
The “__” prefix before the “
mix” and “mixin” keywords indicate that thesefeatures are experimental, and will be finalized when the proposal for mixins is
ready.
Implementation concepts
What does it mean to mix a mixin into a class/mixin?
A mixin is mixed into a class or mixin declaration by adding a mix declaration
as a member. A mix declaration collects all the members of the mixin mentioned
in the mix declaration and conceptually merges it into the list of members of
the enclosing class/mixin.
Duality of
Selfin a mixinThe
Selfin an interface is a type variable, and theSelfof a mixin needsto also behave like a type variable because when a mixin is mixed into a class,
the
Selftype variable needs to behave like the enclosing class type. So weneed to substitute the
Selftype variable with the type of the enclosingclass. In the
Mainfunction of the above example, the type of theSquaremethod accessed through the
pobject of typePointbecomesfn Square[me:Point](x:i32) -> i32after the substitution ofSelftype variable.However, it should also be possible to access other mixin members in a mixin
member method body through the
Selftype. In this way theSelfof a mixinshould behave like the
Selfin a class where it’s just an alias to the type ofthe declaration. Thus, the
Selfof a mixin needs to behave as both theSelftype in a class and the
Selftype in an interface.In this PR, the
Selfin a mixin is aGenericBindingso that it behaves likea type variable. As a result, it doesn’t behave like the
Selfin a class andwithin a mixin member method we can’t access other mixin members through “me”
which has the type
Self. In the final version, theSelfin a mixin will beimplemented as new class that has both the properties of the Self in classes and
interfaces.
Summary of changes
New declaration classes for mixin and mix declarations have been added.
MixinDeclarationis very similar toInterfaceDeclaration. AMixDeclarationholds an expression that will eventually evaluate into a
MixinPseudoType(seenext paragraph). During type checking, a pointer to the evaluated
MixinPseudoTypeis assigned to theMixDeclarationfield namedmixin_value_.The
MixinPseudoTypeclass has been created which is very similar toInterfaceType. This class is the value associated with aMixinDeclaration.Similarly, the
TypeOfMixinPseudoTypehas also been created which is the typeassigned to a
MixinDeclaration. These “values” are not allowed to be used in atype expression.
The new
FindMixedMethodAndTypefunction does the substitution ofSelfwith aconcrete type (as explained in the “duality of
Self” section above) while typechecking class member access expressions.
The
FindFunctionmethod ofNominalClassTypehas been modified to recursivelysearch for the function by following the mixins mentioned in
MixDeclarationmembers.
A
collected_members_field has been added to theTypeCheckerclass thatstores all the direct and indirect members of all class and mixin declarations.
Indirect members are those members which have been exported by a mixin through a
mix declaration.
FindCollectedMembersandCollectMemberare helper methodsthat retrieve members and add a new member for a particular class/mixin
declaration, respectively. When we have mix declarations as members, we traverse
a graph of
MixinDeclarationnodes connected throughMixDeclarationmembersin order to fetch all indirect members of a class/mixin. This list of indirect
members along with direct members will be used to check for name clashes among
all members during the type checking phase. This means that if a
MixinDeclarationnode has multiple in-edges i.e. multiple classes/mixins mixwith the same mixin, then we would have to fetch the indirect members of that
MixinDeclarationmultiple times. We don’t need to perform this indirect memberfetching graph traversal multiple times because
collected_members_acts as amemoized cache that stores the direct and indirect members of all classes/mixins
after they are encountered once during the graph traversal that happens during
type checking. We need not worry about loops forming in the graph at the type
checking phase because the name resolution phase disallows mixing mixins that
haven’t been declared yet.
Mixin features implemented by this PR
Mixin features that are planned for the future
as well as the Self of an interface. This means the body of a mixin method
will be able to access other mixin members.
forclause of a mixin declaration which will add constraintsto another class/mixin trying to mix that mixin. This means that the body of a
mixin method can access interface members that the classes mixing this mixin
implements.
changing the names of the members of mixin declarations
Timeline
- Mixed member access through class instance
- Imports (constraints on entities mixing with a mixin)
- private exports
declarations)
- impls in mixin declarations
- Fields and constructors