Forums

Mixins

General discussion about Cobra. Releases and general news will also be posted here.
Feel free to ask questions or just say "Hello".

Mixins

Postby Charles » Sun Mar 01, 2009 1:41 am

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.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Mixins

Postby Charles » Sun Mar 01, 2009 3:11 am

This blog post covers limitations experienced without mixins, and the various suboptimal workarounds:

http://bloggingabout.net/blogs/erwyn/ar ... /9925.aspx
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Mixins

Postby jonathandavid » Mon Mar 02, 2009 3:19 am

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:

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

Postby Charles » Wed Mar 04, 2009 3:17 am

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:
class Foo
shared
def foo(n as Named)
print n.name
def main
pass
Charles
 
Posts: 2515
Location: Los Angeles, CA

Re: Mixins

Postby jonathandavid » Wed Mar 04, 2009 9:23 am

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

Postby Charles » Wed Mar 04, 2009 10:39 am

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:
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

Postby jonathandavid » Wed Mar 04, 2009 12:38 pm

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:

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

Postby Charles » Wed Mar 04, 2009 1:44 pm

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:
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

Postby hopscc » Mon Mar 09, 2009 10:24 pm

Bug with mixins and props
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

Postby Charles » Tue Mar 10, 2009 4:45 am

I agree this is a bug. Thanks for the report.
Charles
 
Posts: 2515
Location: Los Angeles, CA

Next

Return to Discussion

Who is online

Users browsing this forum: No registered users and 5 guests

cron