10.m - 20.cm + 10.mm - 3.cm == 9780.mm
A mini DSL with Type Classes
Frege makes it easy to embed mini DSLs (domain specific languages) into the application. The language construct that we will use to achieve this are type classes.
The Initial Goal
Let’s assume that our DSL models measurements of distance such that
ten meters should appear as
10.m and we want to do arithmetic
with it like
mm having the usual meaning in the metric system.
|The example is shamelessly stolen from Groovy in Action.|
The initial implementation idea is to represent measurements with the
Int type where
the value of
1 represents one millimeter. This gives us arithmetic for free at the expense
of not having much type support. We will refine that in a future post.
How can we make
1.mm into a value
In "The power of the dot" we have seen that
1.mm is just the dot notation for
XXX.mm 1 where XXX
can be the type or a type class of the following value
1. Our actual
Int type has no
mm function so we are left with using a type class.
class (Integral a) => Millimeter a where mm :: a -> a cm :: a -> a m :: a -> a
You can read this definition as follows:
Any type "a" (provided that it is an Integral type like
can be made a type of class Millimeter by defining functions
m that for a given "a" value return a value of type "a" in millimeters.
|The "a" is called a type variable. It stands in for a type. Like all variables it must be lowercase.|
Now that we have the type class defined, all we need to do is making
Int an instance of this class.
instance Millimeter Int where mm i = i cm i = i.mm * 10 m i = i.cm * 100
And voilà, from now on we can do metrical calculations with ease.
main args = do println $ 10.m - 20.cm + 10.mm - 3.cm == 9780.mm
By making the
Int type a member of the class of
Millimeter types we have given it a new
capability. This was an non-intrusive, incremental change. There was no need for doing any change
to existing code. This is important because we can be sure that we haven’t broken anything.
In a future post, we will build on this initial understanding of type classes and do even more sophisticated modeling to cover not only distance but also time and velocity for proper, type-safe modeling of measurements.
Why functional programming really matters: incremental development