Modeling DSLs with F# and Units of Measure
Very recently on Lambda the Ultimate, they had a really good post describing Soccer-Fun, a way to teach functional programming (pdf). The premise is very simple and is described as the following:
...a domain specific language for simulating football. ... We have used Soccer-Fun in teaching during the past four years. We have also experience in using Soccer-Fun for pupils in secondary education. ... It engages students to problem solving with functional programming because it allows them to compete at several disciplines: the best performing football team can become champion of a tournament; the best written code can be awarded with a prize; students can be judged on the algorithms used. This enables every student to participate and perform at her favourite skill. ... It can be implemented in any functional programming language that supports some kind of windowing toolkit.
This serves as an introductory course in functional programming called “Abstraction and Composition in Programming”. This course covers what you might consider classic functional programming materal including basic types, algebraic data types, high order functions, overloading, recursion and so on. But what’s interesting as well is that it has been applied to secondary school aged students as well. A course such as this which engages through the use of a well known subject such as soccer or football, depending on your nationality of course.
Although the paper discussed here is implemented using the Clean programming language, which has striking similarities to Haskell, another purely functional programming language. As I read the document, I wondered how F# as a language would fare to implement such a DSL, especially with the use of Units of Measure.
Introducing Units of Measure
One feature of F# that was introduced in the September 2008 CTP release of F# was static checking and inference of Units of Measure. The idea is that we have defined in the F# libraries the International System of Units (SI) such as meters, seconds, and kilograms and apply them in ways that they are statically checked and inferred without much ceremony, but as well to create and apply our own such as feet, gallons or whatever we so choose.
For example, I could specify my weight in pounds in F# Interactive as the following:
> [<Measure>] type lb;; [<Measure>] type lb > let weight = 180.0<lb>;; val weight : float<lb> = 180.0
As you can tell from above, it inferred that my weight is in fact a floating point in pounds, and that I weight approximately 180. What’s powerful about this concept is that I cannot then compare it with just another float, it must be of the same type as we note below:
> weight = 180.0;; weight = 180.;; ---------^^^^ stdin(79,10): error FS0001: This expression has type float but is here used with type float<lb>
Andrew Kennedy, one of the forces behind this effort and researcher at Microsoft Research Cambridge, blogged about this in a multi-part series covering:
- Part 1 – Introducing Units
- Part 2 – Unit Conversions
- Part 3 – Generic Units
- Part 4 – Parameterized Types
Now that you’re back, you might ask how this applies in this situation. Can we use this to our advantage when modeling such things as basic elements of soccer using units of measure?
Applying to Soccer Fun
Indeed many of the calculations required for Soccer Fun do require a bit of hard type units. For example, let’s look at how to model such things as specifying the size of the football field. As not all fields are the same size, we can model it in meters such as the following. We’ll note that F# already defines the International System of Units (SI) in the SI module in F# in the FSharp PowerPack assembly. Using F# Interactive, we can get up and running to use these standard measurements by doing the following:
> #r "FSharp.PowerPack.dll" - open Microsoft.FSharp.Math.SI;;
Now getting back to our solution, we can now specify our field dimensions as the following:
type Meter = float<m> type FieldWidth = Meter type FieldLength = Meter type FootballField = { Width : FieldWidth; Length : FieldLength }
I followed the intent closely of the paper to create type aliases for both the Meter as well as the field’s length and width. We can then create a record type to hold both our width and length of our field in floating point meters.
Another example is that both players and the ball both travel at a certain speed and in a certain direction. We need the ability to not only calculate the speed along the surface, but also the speed above the surface in a full three dimensions as well as the direction. We might implement this as the following:
type Radian = float type Angle = Radian type Velocity = float<m/s> type Speed = { Direction : Angle; Velocity : Velocity } type Speed3D = { Speed2D : Speed; Speed3D : Velocity }
Above, I described that our angles will be calculated in radians, and our velocity will be calculated in meters per second. With those two data types in hand, we can then store the speed of the players using the Speed type and the ball using the Speed3D type as it has a tendency to travel in three dimensions whereas the players tend to travel on only two.
Conclusion
This of course is only a start to look at such a concept for how to implement this in F#, but it gives us another tool in our belt to model our data much more accurately in the domain in which we are in. The conciseness of the language makes it an ideal candidate for scientific DSLs where such units of measure are important, but as we can see from this example, it could even apply to the general physics of game play.