I have added initial support for mixins to the language. "mixin" is one of those terms like "object" and "class" that varies a bit from language to language. But essentially, mixins are a limited form of multiple inheritance.
My motivation is that languages such as C# and Java (and formerly Cobra) with their single inheritance, can make coding awkward when you have concerns that cut across the class hierarchy. Historically, I came out of C++ thinking that multiple inheritance was bad, but then later I came out of Python thinking that C++ was bad. Okay, well actually I already thought that before I found Python.
Anyway, there are places in the compiler implementation and my side projects where I could make use of mixins in Cobra. Having successfully used multiple inheritance in Python, I find myself missing it. This is one the few remaining areas where Cobra was really lagging behind Python. And it puts Cobra further ahead of C# and Java.
The upshot for mixins is a reduction in code and a better organized code base.
Note that you can find blog posts and other web pages addressing the limitations of single inheritance in C# and Java. They all involve an awkward manual approach that is heavy on keyboarding and yields crufty code, or they use some kind of run-time object container that dynamically creates new classes combined out of existing ones. I wanted something more natural and clean at the language level.
Note that compared to extensions, mixins allow you to also add state (vars) and properties (pro/get/set). Extensions only support methods.
This feature adds two new keywords to the language. "mixin" for declaring a mixin, and "adds" for adding mixins to a declared type. Note that adding mixins is a compile-time feature in the same way that declaring class inheritance and interface implementation are done at compile-time in declarations.
Here is the initial test case: svn:mixin-basics.cobra:1948
Here will be the latest test cases at any time: svn:Tests/150-mixins
There are lots of restrictions on mixins at this time. They cannot inherit other types or implement interfaces, for example. Over time, as we gain experience with mixins, some of these restrictions will likely be relaxed. What we have now are the very basics:
-- declare mixins
-- support for object vars, properties and methods
-- add one or more mixins to a class
As this is a fresh feature, testing is welcome!
Finally, I have stubbed out a MixIn wiki page which has some information and will grow over time.
Forums
Mixins
13 posts
• Page 1 of 2 • 1, 2
Re: Mixins
This blog post covers limitations experienced without mixins, and the various suboptimal workarounds:
http://bloggingabout.net/blogs/erwyn/ar ... /9925.aspx
http://bloggingabout.net/blogs/erwyn/ar ... /9925.aspx
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Mixins
Thanks for adding this feature, Chuck. I'm not familiar with mixin-based programming myself, but I'm sure it has a lot of benefits. This is a great excuse to learn more about it.
Since the article you linked to uses a single example (timestamped classes), I think if would be nice if you could show us the way this would be done in Cobra. The part I don't see clear is the one that has to do with the constructor. How can I define the "TimeStamped" mixin in such a way that the code that computes the time stamp is called automatically from the constructor of the composite class?
I have another question about mixins. Can I write polymorphic code with them? For example:
I know I should download the code and try it myself, but I don't have time right now.
BTW, this example has reminded of a feature I would like to see in Cobra: "module" classes in which only is shared members are allowed, and in which the "is shared" part is optional:
I don't know if "module" is the right term, but you get my point.
Since the article you linked to uses a single example (timestamped classes), I think if would be nice if you could show us the way this would be done in Cobra. The part I don't see clear is the one that has to do with the constructor. How can I define the "TimeStamped" mixin in such a way that the code that computes the time stamp is called automatically from the constructor of the composite class?
I have another question about mixins. Can I write polymorphic code with them? For example:
mixin Named
var name
class Person adds Named
pass
class Test
def f(n as Named) is shared
print n.name
def main is shared
p = Person()
.f(p) # does this work? I think it should...
I know I should download the code and try it myself, but I don't have time right now.
BTW, this example has reminded of a feature I would like to see in Cobra: "module" classes in which only is shared members are allowed, and in which the "is shared" part is optional:
module Test
def f(n as Named) # is shared
print n.name
def main # is shared
p = Person()
.f(p) # does this work? I think it should...
t = Test() # error, can't instantiate module
I don't know if "module" is the right term, but you get my point.
- jonathandavid
- Posts: 159
Re: Mixins
Mixins currently support object vars, properties and methods. I haven't added support for constructors which would explain why I haven't shown such an example.
Yes, you can use the mixin as a type. In .NET/JVM terms, it's an interface. Cobra shoves the method implementations into a side class with a different name.
I'm currently doing what you describe with "module" like this:
Yes, you can use the mixin as a type. In .NET/JVM terms, it's an interface. Cobra shoves the method implementations into a side class with a different name.
I'm currently doing what you describe with "module" like this:
class Foo
shared
def foo(n as Named)
print n.name
def main
pass
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Mixins
I'm striving to grasp the mixin concept. You say that mixins are a limited form of multiple inheritance (MI). To what extent are they limited, i.e., what kind of things can one do with (MI) that cannot be done with mixins? I understand that MI is very hard for the compiler to implement properly, but what makes mixins easier? Is it that mixin methods cannot be overriden?
- jonathandavid
- Posts: 159
Re: Mixins
Well I never said mixins were easier than multiple inheritance. Cobra is riding on top of .NET and eventually JVM, neither of which support mult inh. So mixins are probably harder because you have to learn more about them. But having them is much easier than being stuck with single inheritance. If you poke around the web looking at how people try to break through the limitations of single inheritance in C# and Java, you can see that Cobra is much easier with the clean syntax and compiler assistance (more details below). That's the "easier" I was talking about.
Now in Cobra, when you say:
It's single inheritance as far as the VM goes. Cobra will never have its own version of the dot operator beyond what the VM provides because Cobra gets its speed from producing idiomatic VM code--the same kind of code that C# and Java produce. By contrast, Python and Ruby each have their own definition of "." which enables them to do more, but also slows them down. So then how does Cobra pull off mixins?
The mixin spawns an interface and the method implementations are injected into any class that adds the mixin. This implies that you need the original source code for a mixin both for the injection and to reflect any updates to the mixin methods. This in contrast to a class that you inherit ("class Circle inherits Shape") where the base class can reside in a binary library (.dll, .class) and updates to that binary will affect you. Say, for example, fixing a bug in a base class method. For a mixin, you would need the source and to recompile.
Another limitation is that the adds clause will only take mixins, not any arbitrary classes. So you have to design your mixins ahead of time.
My current thinking is that in day to day programming, you should be structuring your code in terms of:
(1) single inheritance and composition
(2) interfaces
(3) interfaces and extension methods
(4) mixins where the above are not sufficient
Where the order is in order of preference and frequency used.
Hope that helps,
-Chuck
Now in Cobra, when you say:
obj.foo
It's single inheritance as far as the VM goes. Cobra will never have its own version of the dot operator beyond what the VM provides because Cobra gets its speed from producing idiomatic VM code--the same kind of code that C# and Java produce. By contrast, Python and Ruby each have their own definition of "." which enables them to do more, but also slows them down. So then how does Cobra pull off mixins?
The mixin spawns an interface and the method implementations are injected into any class that adds the mixin. This implies that you need the original source code for a mixin both for the injection and to reflect any updates to the mixin methods. This in contrast to a class that you inherit ("class Circle inherits Shape") where the base class can reside in a binary library (.dll, .class) and updates to that binary will affect you. Say, for example, fixing a bug in a base class method. For a mixin, you would need the source and to recompile.
Another limitation is that the adds clause will only take mixins, not any arbitrary classes. So you have to design your mixins ahead of time.
My current thinking is that in day to day programming, you should be structuring your code in terms of:
(1) single inheritance and composition
(2) interfaces
(3) interfaces and extension methods
(4) mixins where the above are not sufficient
Where the order is in order of preference and frequency used.
Hope that helps,
-Chuck
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Mixins
Thanks, it had not occurred to me that MI is impossible to implement if .NET / JVM compatibility is to be kept. Now I see clearly that mixins are a way to "fake" MI when the backend does not support it.
I had not thought about the "injection" part either. I understand that Cobra mixins will never be usable from other .NET languages. But don't you think it should be possible to (a) create a mixin with Cobra, (b) compile it into a .dll , (c) reuse it in another Cobra program, that has access to the dll only? I gues it would only be a matter of putting the mixin source code inside the DLL, as metadata. I'm just asking this because I think mixins will be much more useful if we are allowed to distribute them inside a dll library (i.e., without having to provide the source code).
I've been investigating a little bit and I've found some elegants examples of mixin usage in Ruby. A particularly nice one involves operator overloading: the user of a class just has to implement a special method (_comp_), then adds the "comp_ops" mixin, and he gets all comparison operators for free. In pseudo-Ruby:
I've deliberately used "eq" instead of "==" because I know Cobra does not support operator overloading yet. But my point is that this seems like a great application of mixins. We could get something similar with inheritance, but this seems more elegant because inheritance offers the wrong abstraction (it would give the impression that "Foo" is an specialization of "comp_ops", which makes no sense).
I've also learned that Scala has mixins too. I guess that's their way of adapting to the fact that the JVM has no MI.
Sorry I digressed a little bit.
I had not thought about the "injection" part either. I understand that Cobra mixins will never be usable from other .NET languages. But don't you think it should be possible to (a) create a mixin with Cobra, (b) compile it into a .dll , (c) reuse it in another Cobra program, that has access to the dll only? I gues it would only be a matter of putting the mixin source code inside the DLL, as metadata. I'm just asking this because I think mixins will be much more useful if we are allowed to distribute them inside a dll library (i.e., without having to provide the source code).
I've been investigating a little bit and I've found some elegants examples of mixin usage in Ruby. A particularly nice one involves operator overloading: the user of a class just has to implement a special method (_comp_), then adds the "comp_ops" mixin, and he gets all comparison operators for free. In pseudo-Ruby:
mixin comp_ops
def eq(other)
return __cmp__(other) == 0 # __cmp__ will be provided by the class that "injects" the mixin
# etc...
class Foo
add comp_ops
def __cmp__(other)
return whatever code that compares this object with "other"
I've deliberately used "eq" instead of "==" because I know Cobra does not support operator overloading yet. But my point is that this seems like a great application of mixins. We could get something similar with inheritance, but this seems more elegant because inheritance offers the wrong abstraction (it would give the impression that "Foo" is an specialization of "comp_ops", which makes no sense).
I've also learned that Scala has mixins too. I guess that's their way of adapting to the fact that the JVM has no MI.
Sorry I digressed a little bit.
- jonathandavid
- Posts: 159
Re: Mixins
The injection problem may be solved with meta data as you mentioned. I'm not opposed to that, but we're not there yet.
I'm also wondering if it could be solved by having the mixed in methods call out to a utility class, generated by the compiler, that contains the mixin methods:
Now the implementation is no longer injected/copied and it can be updated just as with a base class. But the challenge here becomes the the lack of visibility of things like object vars.
Another idea then is to use composition like so:
So there are multiple possibilities and they potentially intersect with issues like inheritance, libraries, generics, methods, initializers, operator overloads, dynamic binding, etc.
Yes, Ruby's approach to operator overloads with mixins is nice, but on the Cobra side we have to consider how this interacts with .NET's existing facilities for op overloads.
By the way, Cobra already invokes .compareTo() for < <= => > and .equals() for == and <>. So providing those methods at least gives you nice comparison operators.
I'm also wondering if it could be solved by having the mixed in methods call out to a utility class, generated by the compiler, that contains the mixin methods:
class Foo adds Bar
# generated by compiler:
def barMethod
BarMembers.barMethod(this) # call out to generated class holding the mixin methods
Now the implementation is no longer injected/copied and it can be updated just as with a base class. But the challenge here becomes the the lack of visibility of things like object vars.
Another idea then is to use composition like so:
class Foo adds Bar
# generated by the compiler:
var _mixin_Bar = Bar(this)
def barMethod
_mixin_Bar.barMethod
So there are multiple possibilities and they potentially intersect with issues like inheritance, libraries, generics, methods, initializers, operator overloads, dynamic binding, etc.
Yes, Ruby's approach to operator overloads with mixins is nice, but on the Cobra side we have to consider how this interacts with .NET's existing facilities for op overloads.
By the way, Cobra already invokes .compareTo() for < <= => > and .equals() for == and <>. So providing those methods at least gives you nice comparison operators.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
Re: Mixins
Bug with mixins and props
Gives compilation error
I cant see why this ( using the 'from' form shortcut syntax in a mixin) should not be supported since its the simplest properties definition syntax...
Failing that the compilation error message is definitely wrong (inside a mixin definition not an interface definition).
- Code: Select all
mixin TimeStamp
#pro stamp from var as DateTime? # same error as below
var _stamp = DateTime.now
pro stamp from _stamp
class MyThing
adds TimeStamp
def init
pass
def main is shared
t = MyThing()
now = DateTime.now
assert t.stamp == now
Gives compilation error
- Code: Select all
mixinbug.cobra(4): error: mixinbug.cobra(4,17): error: Cannot use the "from" form of a property inside an interface declaration.
Compilation failed - 1 error, 0 warnings
I cant see why this ( using the 'from' form shortcut syntax in a mixin) should not be supported since its the simplest properties definition syntax...
Failing that the compilation error message is definitely wrong (inside a mixin definition not an interface definition).
- hopscc
- Posts: 632
- Location: New Plymouth, Taranaki, New Zealand
Re: Mixins
I agree this is a bug. Thanks for the report.
- Charles
- Posts: 2515
- Location: Los Angeles, CA
13 posts
• Page 1 of 2 • 1, 2
Who is online
Users browsing this forum: No registered users and 9 guests