Summary
The paper "A First-Class Approach to Genericity" introduces the idea of the mixin, which seems to be quite a powerful concept, and then shows that you can't do it with Java generics. I wonder if anyone has any alternative approaches to this problem.
(Also see the follow-on to this article, Mixins 2.
The term mixin seems to have acquired numerous meanings over time, but the fundamental concept is that of mixing in capabilities in the same way that you would mix adornments like cookies and candy pieces into ice cream. Although these things are reasonably well-blended, you can still see evidence of their having been mixed in. More importantly, it's something you do at the last minute, which makes it convenient to easily assemble classes.
One value for mixins is that they consistently apply characteristics and behaviors across multiple classes.
When you look at the way that it is used, the concept of a mixin seems very closely related to the Decorator design pattern. Something is decoratable, and you layer on functionality by wrapping other classes around the decoratable. Decorators, however, are implemented using composition and formal structures (the decoratable/decorator hierarchy), whereas mixins are inheritance-based. So you could think of parameterized-type-based mixins as a generic decorator mechanism that does not require the inheritance structure of the Decorator design pattern.
Because the resulting class contains all the methods of interest just like any other class, the mixin approach is arguably more natural than a decorator, because the type of the object that results from using decorators is the last type that it was decorated with, whereas the type of the mixin is all the types that have been mixed together.
One of the strongest arguments made for multiple inheritance in C++ is for the use of mixins. However, a more interesting and elegant approach to mixins is using parameterized types, whereby a mixin is a class that inherits from its type parameter. In C++, you can easily create mixins because C++ remembers the type of its template parameters.
Here's a C++ example with two mixin types: one that allows you to mix in the property of having a time stamp, and another that mixes in a serial number for each object instance:
//: generics/Mixins.cpp
#include <string>
#include <ctime>
#include <iostream>
using namespace std;
template<class T> class TimeStamped : public T {
long timeStamp;
public:
TimeStamped() { timeStamp = time(0); }
long getStamp() { return timeStamp; }
};
template<class T> class SerialNumbered : public T {
long serialNumber;
static long counter;
public:
SerialNumbered() { serialNumber = counter++; }
long getSerialNumber() { return serialNumber; }
};
// Define and initialize the static storage:
template<class T> long SerialNumbered<T>::counter = 1;
int main() {
TimeStamped<SerialNumbered<string> > mixin1, mixin2;
mixin1.append("test string 1"); // A string method
mixin2.append("test string 2");
cout << mixin1 << " " << mixin1.getStamp() << " " <<
mixin1.getSerialNumber() << endl;
cout << mixin2 << " " << mixin2.getStamp() << " " <<
mixin2.getSerialNumber() << endl;
} /* Output: (Sample)
test string 1 1129775318 1
test string 2 1129775318 2
*///:~
Notice that the resulting type has all the methods of the mixed-in types. You can think of a mixin as a function that maps existing classes to new subclasses.
Unfortunately, Java generics don't permit this. Erasure forgets the base-class type, so a generic class cannot inherit from one of its generic parameters.
//: generics/Mixins.java
// {CompileTimeError}
import java.util.*;
// Can't inherit from a generic parameter:
public class TimeStamped<T> extends T {
public final long timeStamp;
TimeStamped() {
timeStamp = new Date().getTime();
}
}
public class SerialNumbered<T> extends T {
private static long counter = 1;
public final long serialNumber = counter++;
}
public class Mixins {
public static void main(String[] args) {
SerialNumbered<TimeStamped<String>> mixin =
new SerialNumbered<TimeStamped<String>>();
}
} ///:~
So here's the question: is there some other way to perform something that produces this kind of mixin cleverness, or has erasure again foiled us?
Hmm. I don't want to rant here, but in the Zope world, (Zope 2 that is) we relied on the "Mixin" pattern a lot. A couple of years later we found out that it's not that convenient if you do it all around the place...
One can use inner classes to fake out mixins in Java. It takes more code than a true mixin would, but it's the best solution I've seen (and also solves 99.9% of the problems where one *really* does want multiple inheritance).
Let's assume primary class C, abstract mixin class M, and mixin interface I. M implements I and declares abstract protected methods to access data from the classes it is to be mixed into. So, to mix M into C, declare C to implement I and create anonymous inner class, AM, extending M and implementing the protected methods. Then in C, delegate functionality to implement I to AM.
> This is the first time I've heard that called a "mixin." > It's the 'Curiously Recurring Template Pattern.' > > Jim Coplien wrote it up years ago.
No, that's different. In CRTP, a new class inherits from a template instantiation that takes the new class as a parameter, like this (from Thinking in C++ Volume 2):
class CountedClass : public Counted<CountedClass> {}; class CountedClass2 : public Counted<CountedClass2> {};
The CRTP is more like the Template Method pattern, whereas mixins are a more elegant form of a Decorator.
From the paper I cited in the article: "Nearly 20 years ago, the Lisp object-oriented community invented the term mixin to describe a class with a parametric parent..."
I think your complaint is the main argument in favor of Aspect Oriented Programming (AOP). With AOP, you'd be able to do exactly what you want, namely apply methods from various sources to a composite class.
It wouldn't be inheritance-based, as you desire, but it would share the characteristics of the mixin example you give. AOP has the added advantage of being able be apply methods (or aspects) declaratively (via XML or annotations).
Of course, since all AOP implementations are currently post-processing driven, they have a certain manageability problem (PITA debugging, restrictions on execution environment, etc.) and a generally kludgy feel.
The only other alternative here is the dynamic proxy. Once you have an interface including all the interfaces of the mixins you want to include, the implementations are delegated to by the invocation interface. Also quite kludgy and generally alot more work than is normally necessary.
Aspects are a much better way of defining mixins in the Java world, but as someon else already mentioned they also have their drawbacks.
There is no nice, neat method to do mixins in Java.
# Traits have the following properties. A trait provides a set of methods that implement behaviour. # A trait requires a set of methods that serve as parameters for the provided behaviour. # Traits do not specify any state variables, and the methods provided by traits never access state variables directly. # Classes and traits can be composed from other traits, but the composition order is irrelevant. Conflicting methods must be explicitly resolved. # Trait composition does not affect the semantics of a class: the meaning of the class is the same as it would be if all of the methods obtained from the trait(s) were defined directly in the class. # Similarly, trait composition does not affect the semantics of a trait: a composite trait is equivalent to a flattened trait containing the same methods.
This gives something like mixins but simpler and still about as useful. Probably the most annoying thing about Java is the inability to add implementation to interfaces that could be generic. It results in duplication of code.
I find the concept of mixins to be a design flaw. The problems with mixins (as stated in the cited article) are that you are combining two types that do not know of eachother. The effect is similar to that of multiple inheritance, but without the explicit relation multiple inheritance implies.
Multiple inheritence of course also has it's share of problems, but one can make the language such that it fails on encountering them, or requiring explicit resolves. Mixin's introduce unexpected behaviour on unsuspecting code that does not know about the combination and the issues that can be caused.
> > This is the first time I've heard that called a > "mixin." > > It's the 'Curiously Recurring Template Pattern.' > > > > Jim Coplien wrote it up years ago. > > No, that's different. In CRTP, a new class inherits from a > template instantiation that takes the new class as a > parameter, like this (from Thinking in C++ Volume 2): > > class CountedClass : public Counted<CountedClass> {}; > class CountedClass2 : public Counted<CountedClass2> {};
C# 3.0 has something that reminds me of mixins: extender methods. You create a class, and add a static method that is attached to an existing class. Dumb example: I want to add a new method HasEvenLength to the string class:
public class MyExtenders { public static bool HasEvenLength(this string s) { return s.Length % 2 == 0; } }
Which can then be used like this:
string myString = "some string"; bool even = myString.HasEvenLength();
Those appear to be what are called "Multimethods," which you can also find in the Nice language. But yes, another way to paste methods into an existing class. There seem to be a number of approaches to this, and I've never seen a comparison -- they tend to be expressed independently, without a sense of "what would mixins give you that multimethods wouldn't," etc.
C# does a kind of "partial erasure" which is not as severe as Java's erasure. In C#'s case it is because generics have to work across the CLR, with languages that may not support generics.
Flat View: This topic has 27 replies
on 2 pages
[
12
|
»
]