« Fear and loathing in Scala 2.7.2 for .NET | Main | Scala for C# programmers, part 1a: mixins and traits, behind the scenes »
January 02, 2009
Scala for C# programmers, part 1: mixins and traits
Okay, so despite the pain of getting it working on .NET, there's still a lot of buzz around Scala. Why is that?
Scala is a hybrid of functional and object-oriented languages. Its functional aspects make it very expressive when writing algorithmic code, and play nicely with the brave new world of concurrency; its object-oriented aspects keep it familiar and convenient when creating business objects or other stateful models. Moreover, because it embraces object-orientation as a first-class idiom, Scala is able to use platform (Java or .NET) libraries very naturally, and conversely programs written in C# or VB.NET can easily use assemblies written in Scala.
That said, remember that Scala's primary platform is the Java virtual machine, and some of the interest in Scala comes from Java programmers' interest in features such as type inference, comprehensions and lambdas, with which C# 3 programmers are already familiar. So what's left that might be of interest specifically to C# programmers?
I was going to answer this with a big long list, but it started turning into an epic, so instead I'm going to take it in smaller doses, starting with mixins and traits.
Motivation
Interfaces in C# and Java play a dual role. First, they are a set of capabilities that an implementer has to, well, implement. Second, they are a feature set that a client can use. These two roles are at loggerheads. The first means that interfaces want to be minimal, so that implementers don't have to implement a whole lot of superfluous and redundant guff. The second means that interfaces want to be maximal, so that clients don't have to clog themselves up with boilerplate utility methods.
Consider, for example, IEnumerable (and its sister interface IEnumerator). This is a very minimal interface: implementers just need to be able to produce values in sequence. But this minimalism means that clients of IEnumerable need to write the same old boilerplate again and again and again: foreach loops to filter, foreach loops to call a method on each element of the sequence, foreach loops to aggregate, foreach loops to check whether all elements meet a criterion, or to find the first member that meets a criterion, or... This is frustrating because the implementations of "filter," "apply," "aggregate," and so on are always the same. Of course, we could put these methods into concrete types (List<T> includes several), but then those concrete types will contain duplicate code, and users who only have an IEnumerable will still miss out. And yet we can't put these methods into the interface because then every implementer of IEnumerable would have to implement them -- and they'd end up writing the same boilerplate, just now in all the zillions of IEnumerable classes instead of their clients.
The C# and Scala Solutions
We could resolve this tension if we had a way for interfaces to contain implementation: for example, if IEnumerable required the implementer to provide the class-specific iteration functionality, but then provided the standard implementations of "filter," "apply", "aggregate" and so on automatically:
public pseudo_interface IEnumerable
{
IEnumerator GetEnumerator(); // must be implemented
IEnumerable Filter(Predicate predicate) // comes for free
{
foreach (object o in this)
if (predicate(o))
yield return o;
}
}
C# 3 addresses this using extension methods: the methods mentioned above are all in fact included as extension methods on IEnumerable<T> as part of LINQ. This has some advantages over the approach described above: specifically, the "standard methods" aren't bound up in the interface, so you can add your own methods instead of being limited to the ones that the interface author has included. On the other hand, it means that method implementations have to be packaged in a different class from the interface, which feels less than modular.
Scala takes a different approach. A Scala trait can contain a mix of abstract interface and concrete implementation. (It can also be a pure interface.) Here's a Scala trait that represents objects that can be compared and ordered:
trait Ord {
def < (that: Any): Boolean
def <=(that: Any): Boolean = (this < that) || (this == that)
def > (that: Any): Boolean = !(this <= that)
def >=(that: Any): Boolean = !(this < that)
}
Orderable objects need to extend Ord, but only need to implement <. They then get the other operators for free, implemented automatically by Ord in terms of <.
class Date extends Ord {
def < (that: Any): Boolean = /* implementation */
}
// can now write myDate >= yourDate
The term traits and mixins (because you are "mixing in" the functionality of Ord into Date) are both used in Scala: I think trait refers to the interface, and mixin to when a class extends a trait containing concrete implementation, but I may be missing the nuances of the exact usage.
Scala Traits vs. C# Extension Methods
Okay, so Scala has a different way of packaging standard implementations from C#'s extension methods. It's different, but why is it interesting? Well, there are a couple of things that you can do with Scala traits that don't fall nicely out of the extension methods approach.
First, you can override the default implementations of trait members, to take advantage of additional information or capabilities available in the implementing type. Let's look at another IEnumerable example, recast as a Scala trait:
trait Enumerable {
def getEnumerator(): Enumerator
def count: Int = {
// Shockingly bad style; for illustrative purposes only
var c = 0
val e = getEnumerator()
while (e.moveNext()) c = c + 1
c
}
}
This (ignoring style issues for now) is the only fully general implementation we can provide for count: it will work with any Enumerable. But for collections that know their sizes, such as arrays or List<T>, it's gruesomely inefficient. It iterates over the entire collection, counting elements one by one, when it could just consult the size member and return that. Let's fix that:
class MyList extends Enumerable {
private var _size;
def getEnumerator(): Enumerator = /* ... */
override def count: Int = _size
}
The count member of the Enumerable trait works like a virtual method: it can be overridden in classes which implement/derive from Enumerable.
Compare this to the Count() extension method on IEnumerable<T> in LINQ. This achieves the same effect by trying to cast to ICollection, which is fine as far as it goes but isn't extensible. Suppose you create an enumerable class that can count itself quickly but isn't a collection -- for example a natural numbers range object. With a Scala trait, the NumberRange type could provide an efficient override of count, just like any other virtual method; with C# extension methods, Enumerable.Count() would have to somehow know about the NumberRange type in advance, or fall back on counting elements one by one.
Second, with Scala you can choose a trait implementation when you instantiate an object, rather than having it baked in at the class level once and for all. Suppose you're creating a MyList instance, but you want it to puff itself up to look bigger so as to frighten other MyList instances off its territory. (This example would probably work better with fish, but we're stuck with Enumerables now. Work with me here.) In C#, you'd need to create a PuffedUpMyList class and override the Count property. In Scala, you can just mix in a PuffedUp version of the trait:
trait PuffedUp extends Enumerable {
override def count: Int = super.count + 100
}
val normal = new MyList
Console.WriteLine(normal.count) // meh
val puffedUp = new MyList with PuffedUp
Console.WriteLine(puffedUp.count) // eek!
As you can imagine this gives us much better granularity and composability of traits and implementations than we get from the extension methods approach, or indeed from single implementation inheritance type systems in general.
So Scala traits have some distinct advantages over extension methods. The only downside appears to be the inability for clients to add their own methods to a trait after the fact. Fortunately, you can work around this in Scala using its implicit conversions feature. The delightfully named Pimp My Library pattern effectively gives us back extension methods, albeit less elegantly, giving Scala programmers the best of both worlds.
January 2, 2009 in Software | Permalink
TrackBack
TrackBack URL for this entry:
https://www.typepad.com/services/trackback/6a00d8341c5c9b53ef010536aab07b970c
Listed below are links to weblogs that reference Scala for C# programmers, part 1: mixins and traits:
Comments
Extension methods, traits/mixins and interfaces are nothing but kludges. They could all be eliminated if C#/Scala had proper multiple inheritance.
I don't understand why people create so much complicated language constructs, where an inheritance from multiple base classes works perfectly in C++ and solves neatly all the problems.
What C#/Scala needs is just ability to inherit implementations, not only interfaces from multiple base classes. Unfortunately, single inheritance of all these "modern" languages prevents that.
Many people somehow got this notion of "multiple inheritance is evil", and "a source of problems". Well, I tell you that in my many years of using C++ it has been a massive help allowing to create clean code architecture. Never had any problems with it!
And as I'm now mainly doing C#, every now and then I stumble upon a case where I have to do ugly tricks to foobar my way around lack of multiple inheritance when designing my code.
This simply drives me nuts - it has been shown 15 years ago that IT WORKS (read some Booch books for details), and it is used AS YOU READ THIS POST in billions of lines of C++ source code out there in the wild.
Why not just do it and skip interfaces (bleee) or these Scala new inventions that noone will understand?
Posted by: kaalus at Nov 1, 2009 7:22:53 AM
That's pretty right. Traits are basically mixin classes, and the whole thing can be perfectly done with multiple inheritance. OTOH, to designate a special language construct for what is good in multiple inheritance is not a bad idea, especially when it helps avoiding what is bad in multiple inheritance :)
Posted by: Andreas Manessinger at Dec 29, 2009 3:22:18 AM
I have to agree with kaalus - if some people in 'outsourcing countries' cannot hack multiple inheritance in C#, well, IT'S THEIR EFFING PROBLEM. The rest of us shouldn't suffer for these dumbing-down decisions.
Posted by: dmitri at Jul 17, 2010 11:48:52 PM
Consider the fact that that Scala is targeted at the JVM and CLR, neither of which support Multiple Implementation Inheritance. Just because it can't be instantiated, doesn't mean it's not a type. This "Kludge", as you call it, is far ahead of Java, which doesn't allow you to use Multiple Implementation Inheritance at all.
Posted by: Anonymous at Aug 25, 2010 6:28:58 AM
Nice explanation of Scala's traits. It seems to implement traits decently considering the limitations of JVM and CLR. I've read much about the problems of multiple inheritance, and how certain implementations of traits can avoid some or all of those. Keep up the quality writing!
Posted by: Dan Vanderboom at Oct 7, 2010 5:10:28 AM
C# has a feature called extension methods which allows for method implementations on interfaces. This means that only the minimal set of members which satisfy the extension methods need to be defined. Boilerplate code need not apply.
Posted by: Rick Minerich at Oct 12, 2010 12:41:29 PM
You do make a good point on the efficiency of custom implementations. Sorry for not reading further. It was the phrasing of the beginning of your post that made me think you didn't know about extension methods.
Posted by: Rick Minerich at Oct 12, 2010 12:43:55 PM
Very good article thanks.
I have a question: if you need an interface that has some bits of implementation that can be overridable, why not using an abstract class, which contains abstract methods as an equivalent for the interface methods, and virtual methods as an equivalent to the implementation methods that you may want to override?
I really like kaalus comment about how multiple inheritance solves all these problems. However C++ has other problems which I wouldn't want to experience just because of my need of multiple inheritance.
Thus, why not creating a variant of C# that allows multiple inheritance? There are open source compilers out there which can be easily modified, and despite the problem that the CLR doesn't natively support multiple inheritance, you can always emulate it at an IL level (kind of cumbersome, but only for the language designer, not for the language consumer).
Posted by: Andres G. Aragoneses at Oct 20, 2010 10:31:23 PM
Hi Andres,
What you describe is basically what a trait is. And traits basically provide a limited form of multiple inheritance -- not full MI a la C++ and kaalus, but simpler in practice and sufficient for many requirements.
There's no reason somebody couldn't implement a C#-like language which supported traits or indeed MI on the CLR (albeit, as you note, at the language level rather than the CLR level): Scala.NET already does this for traits with a non-C#-y syntax (and there's an Eiffel port which has full MI). Just a matter of someone putting in the hard yards I guess!
Posted by: Ivan at Oct 21, 2010 7:18:28 AM
First, I know I'm very late to this discussion.
Second, I'm a C# programmer, and a Scala newbie. But I would like to add that a seriously doubt that Scala's trait implementation is inferior to MI. Everything that one can do using MI s/he can also do with traits...
The only big difference is with `super` resolution - the Scala's implementation forces an linear, "stackable" inheritance resolution, and so avoids the "diamond inheritance" problem, typical of traditional MI languages.
Third, @dmitri: I am from an "outsourcing country". Also I've used to be a C++ programmer, and I guess that I have an above-average understanding about both C++ MI mechanism and Scala's mixins. So, please, go fly a kite.
Posted by: Rogerio Senna at Jan 25, 2011 9:21:34 AM