Sunday, December 13, 2009

Inheritance nightmares

I was first exposed to object-oriented programming when learning C++. I am now starting to realize how confusing that has been. In particular, inheritance in C++ is often used and abused to implement various kinds of relationships between classes. I am writing "abused", because the only legitimate use of inheritance is for IS-A relationships. Other relationships such as HAS-A and IS-IMPLEMENTED-IN-TERMS-OF should not use inheritance. In these cases composition is preferable. It is therefore important to be able to differentiate between the different kinds of relationships.

However understanding IS-A relationships is not as easy it might seem. To illustrate the problem, the case of squares and rectangles is sometimes used.

All squares are also rectangles, and it seems natural that a class "Square" should be related to a class "Rectangle": Square IS-A Rectangle.

In C#, this could be written:

class Rectangle
{
}

class Square : Rectangle
{
}


Now that we have our class hierarchy, let us start adding methods and fields:


class Rectangle
{
float length;
float width;

public Rectangle(float l, float w)
{
length = l;
width = w;
}

public float GetLength() { return length; }
public float GetWidth() { return width; }
}


So far, no problem. Things get tricky when we want to add setter methods:


class Rectangle
{
float length;
float width;
...
public void SetLength(float l) { length = l; }
public void SetWidth(float w) { width = w; }
}

class Square : Rectangle
{
...
public void SetSize(float s) { SetLength(s); SetWidth(s); }
}


The problem is that Square inherits SetLength and SetWidth, which makes it possible to have squares with non-matching lengths and widths. A non-satisfactory solution consists of overriding SetLength and SetWidth in Square:


class Rectangle
{
float length;
float width;
...
public virtual void SetLength(float l) { length = l; }
public virtual void SetWidth(float w) { width = w; }
}

class Square : Rectangle
{
...
public override void SetLength(float s) { base.SetLength(s); base.SetWidth(s); }
public override void SetWidth(float s) { base.SetLength(s); base.SetWidth(s); }
}


This solution is not satisfactory because users of Rectangle may expect that it should be possible to set widths and lengths independently.


void SetWidthAndLength(Rectangle rect)
{
rect.SetWidth(1.0);
rect.SetLength(2.0);
Debug.Assert(rect.GetWidth() == 1.0);
Debug.Assert(rect.GetLength() == 2.0);
}


These assertions fail when SetWidthAndLength is passed an instance of Square. Many articles go on by stating that Rectangle and Square are in fact not IS-A related, although the initial intuition told otherwise. This is usually the point where the Liskov Substitution Principle is introduced. While understanding this principle is certainly a good thing for anyone learning about object-oriented programming, one of the biggest selling points of object-oriented design is that it is supposed to be intuitive.

After all, all squares are rectangles, aren't they? Indeed, this is true of immutable squares and rectangles. Remove the setters, and all problems disappear. The substitution principle falls in line with what intuition tells us.

Going a bit further, let us consider a function which transforms squares.


Square TransformSquare(Square square, SquareScaling scale)
{
float scaleFactor = ...;
return scale.Do(square, scaleFactor);
}


If we have at hand a library which provides translation and rotation operations on rectangles, we are very close to being able to use these with "Transform". All we need is to write a bit of code to turn rectangles which have identical lengths and widths into squares:


class RectangleUniformScalingAdapter : SquareScaling
{
RectangleUniformScaling adaptee;
...
public override Square Do(Square square, float factor)
{
Rectangle tmp = adaptee.Do(square, factor, factor);
Debug.Assert(tmp.GetLength() == tmp.GetWidth());
return new Square(tmp.GetLength());
}
}


On the other hand, if we are provided a function working on rectangles, a library providing transformations on squares would not be very valuable.

In other words, we could say that RectangleUniformScaling IS-(almost)-A SquareScaling. Note how the IS-A relationship is reversed.

If different parts of the interfaces of Rectangle and Square do not agree on the direction of the relationship, we have a problem.


class Rectangle
{
float width;
float length;

public virtual float GetWidth() { /* Easy to implement */ }
// Uniform scaling
public virtual void Scale(float scale) { /* could use Square.Scale() */ }
public virtual void Scale(float scaleWidth, scaleLength) {...}
}

class Square
{
public virtual float GetWidth() {/* could use Rectangle.GetWidth() */ }
public virtual void Scale(float scale) { /* Easy to implement */ }
public virtual void Scale(float scaleWidth, scaleLength) { /* Impossible to implement */ }
}


Who should inherit from who?

At this point, readers interested in F# and functional programming languages might wonder what all this has got to do with F#. I think that the common mix of mutability and inheritance is not a very strong basis for good software design. I never really realized that until I took a look at functional programming and immutability.

Sunday, December 6, 2009

A bit of F# in your C#

At work, I'm using way more C# than F#.

I have however noticed that functional programming concepts from F# have found their way into my C# code. The first sign of this is when LINQ extension methods start appearing where foreach loops used to be.

F# has a higher-order function called "fold" which can be used for collecting information from a sequence into a single object. A common use is for summing, but it can also be used for extracting one item from the sequence. For instance, it can be used for finding the minimal item according to a specific ordering.

This is a problem Jon Skeet has tackled in a blog from 2005 entitled A short case study in LINQ efficiency.

For instance, given a sequence of pairs of string*number denoting names and ages of people, find the youngest person. Note that we are not only interested in finding the age of the person, we want both the name and the age.

Here is a solution in F# which uses "fold".


let people = [("me", 32.0); ("someoneelse", 75.0)];;
let cmp a b =
match a, b with
| Some (_, a1), (_, a2) when a1 <= a2 -> a
| _ -> Some b;;

let youngest = List.fold cmp None people;;


Function "fold" iterates over each item in the list. For each item, it invokes a function to which it passes the item together with a value called the accumulator. The function returns the new value for the accumulator.

In the example above, the type of the accumulator is "Some person or None", where "None" denotes "no one".

Function cmp simply compares an accumulator with a person, and returns the person if the accumulator is "None", the youngest of the person and the accumulator otherwise.

This solution, when translated to C# 4.0, gives an elegant and efficient solution. I wonder why Jon Skeet did not mention it. If he missed it, he's not alone, a search on Google for how to implement one's own LINQ extension method shows many articles using precisely this problem as illustration.


var people = new Tuple<string, double>[] {
Tuple.Create("me", 32.0),
Tuple.Create("someoneelse", 75.0) };

var youngest =
people.Aggregate(null as Tuple<string, double>,
(youngestSoFar, v) =>
(youngestSoFar != null && youngestSoFar.Item2 <= v.Item2) ?
youngestSoFar : v);


I haven't done any time measurements, but this solution should iterate over the array of people no more than once, and might even be just as efficient as the method using foreach.

F# on the Xbox 360

This article describes how to build an XNA Game Studio application using an F# library in such a way that it can be run on the Xbox 360. It applies to the current version of F#, 1.9.7.8

A method was first described in this blog by Jack Palevich. Most of his blog post still applies, except for step 2.

In order for an .net assembly to be usable on the Xbox 360, it must be linked against dlls for the Xbox 360, which can be found in <Program Files directory>\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360.

In particular, it must be linked against mscorlib.dll for the Xbox 360, which is the difficult part.

By default, when you create an F# library in Visual Studio, it is linked against FSharp.Core.dll, which references mscorlib for the PC.

In order to avoid conflicts between versions of mscorlib, one must build an F# library targeted at the Compact Framework 2.0, which is what the XNA .net framework is built upon.

The problem is that neither Visual Studio nor msbuild support this for F# libraries. The only solution is to compile the F# library using the F# compiler directly:


"C:\Program Files\FSharp-1.9.7.8\bin\fsc.exe"
-o:obj\Release\XBox360.dll
--standalone --noframework --define:TRACE --optimize+ --tailcalls+
-r:"C:\Program Files\FSharp-1.9.7.8\CompactFramework\2.0\bin\FSharp.Core.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\Microsoft.Xna.Framework.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\mscorlib.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\System.Core.dll"
-r:"C:\Program Files\Microsoft XNA\XNA Game Studio\v3.1\References\Xbox360\System.dll"
--target:library --warn:3 --warnaserror:76 --fullpaths --flaterrors
<your F# source files here>


Beside specifying the correct XNA assemblies using "-r:", this command line also does the following:

- Enables optimization using --optimize+ and --tailcalls+
- Embeds the F# core library in the assembly, using --standalone

Not very pretty, and hopefully future versions of F# and its integration into Visual Studio will remove the need to call fsc.exe explicitly.