Friday, July 20, 2012

Movimentum - Operators on interfaces do not work: A quick fix

The major change from the last posting was the introduction of interfaces for (most) expression types. This made it necessary to replace constructor calls for constants and variables with calls to factory methods whose return type is one of the new interfaces. The change from classes to interfaces ripples through the whole software, but thanks to ReSharper, it is quickly done.

Everything looks nice, until an intermediate compile suddenly turns up an bad problem: Operators cannot be defined solely on interfaces!

The problem is visible in many test cases, but also in some rewrite rules, where we could write

     new Constant(1) + new Constant(2);

until now, whereas

    Polynomial.CreateConstant(1) + Polynomial.CreateConstant(2)

is no longer possible: The + operator is defined for parameters of class AbstractExpr, but the results of the CreateConstant calls are IAbstractExprs! When we try to replace the parameters of operator+ with IAbstractExpr, the compiler tells us in no uncertain terms that at elast one of the parameters must be of the surrounding class.

What can we do?

Here is my quick fix: We add a property C to IAbstractExpr which returns the same object, but now as an AbstractExpr. The implementation, in class AbstractExpr, is trivial:

    public AbstractExpr C { get { return this; } }

The 1 + 2 example can now be written as follows, which happily compiles:

    Polynomial.CreateConstant(1).C + Polynomial.CreateConstant(2).C

To get rid of about half of these .Cs, C# allows us to pass in one interface parameter:

    public static AbstractExpr operator +(AbstractExpr lhs, IAbstractExpr rhs) {
        ...
    }

The example from above can now be written

    Polynomial.CreateConstant(1).C + Polynomial.CreateConstant(2)

As a side note, this is an example where interfaces are not introduced to be implemented by new classes. The design of the solver model is "closed" in the sense that no effort goes into considerations how to write new classes that implement one of the interfaces outside the controlled design of the solver engine. Therefore, there is no problem with the C property: This is not an abstract request to convert any sort of IAbstractExpr to an AbstractExpr; rather, it is simply the technically helpful statement that every IAbstractExpr object is an AbstractExpr object, anyway, and that we want to expose this simple fact because of C#'s operator limitation.
After this side note—that could be expanded to a whole article on how interfaces can be used; and what "abstraction" can mean—, we return to the mundane job of liberally sprinkling our code with .Cs, until it compiles again.

No comments:

Post a Comment