Chapter 46. Literal Extension

Add methods to program literals.

 42.grams.flour

46.1 How It Works

Literals, such as numbers and strings, often make a good starting point for DSL expressions. Traditionally, however, they are built-in types with fixed interfaces, so you can’t extend them. More languages now allow you to add methods to third-party classes using techniques like C#’s extension methods and Ruby’s open classes. This capability is particularly handy for DSLs, as it allows you to start a method chain with a literal.

As with most method chains, one important decision is whether to use an Expression Builder. If you don’t use an Expression Builder, you have to ensure that all the intermediate types have the appropriate fluent methods defined on them. Using an Expression Builder avoids this, but you have to ensure you can get from the builder to the underlying object cleanly.

Take the expression 42.grams. What should be the result type of this? I see three main options: a number, a quantity, or an Expression Builder. With a number, you generally choose one unit to be your canonical unit, for example with weight you might use kilograms. In this case, 42.grams would result in 0.042, and 2.oz would yield 0.567.

One thing to watch for here is what my colleague Neal Ford calls type transmogrification. 42.grams starts with an integer, but turns into a floating point. This means that all the further methods in the chain have to be defined on multiple numeric types.

With a quantity, 42.grams turns into a quantity object with a magnitude of 42 and a unit of grams. In general, I much prefer quantities to simple numbers for representing dimensioned values; quantities represent my intent better and also allow me to define useful behavior (such as alerting me to problems with 42.grams + 35.cm). Sadly, almost all language platforms lack a built-in quantity class, but at least you can easily define it yourself with any fluent methods you need. Since the magnitude of the quantity is encapsulated, you greatly reduce the type transmogrification problem, because all the following methods are defined on quantity. However, quantity still has the fluent methods which may make the quantity class harder to understand.

The final option is to use an Expression Builder, so 42.grams would yield a recipe builder instance. At this point, you can use one or more Expression Builders and have full control over how the rest of the expression works. The problem here is that you need to ensure the calling code can easily unpack the subject from the builder. This isn’t a problem for expressions such as:

image

but is a problem if you want an expression like 42.grams + 3.oz. I tend to prefer an Expression Builder most of the time, but it really depends on the context of its use.

46.2 When to Use It

Literal Extension has become a popular illustration of how to make APIs more fluent, particularly by advocates of languages which are able to do it. The ability to add methods to third-party classes was not something supported in mainstream OO languages (although Smalltalk always made it possible). It can help a good deal in improving fluency, although there’s also the suspicion that some of this enthusiasm is fondness of a new toy.

In some environments, there is a serious concern that adding methods like this to literals will bloat the interface of those literal classes. These Literal Extensions are only needed in some contexts, so if they appeal in more contexts they can make a class’s interface much more confusing. If this is the case, then you have to weigh the usefulness of the Literal Extension versus the problems it adds by complicating the literal class’s interface. Some language environments allow you to state that Literal Extensions are bound to a namespace, which avoids this problem.

46.3 Recipe Ingredients (C#)

Without stressing my creativity any further, I decided to steal this example from my colleague Neal Ford, who’s been using it in several articles and talks. It’s simply a C# formulation of the sketch.

var ingredient = 42.Grams().Of("Flour");

For this case, I’ll use domain types rather than an Expression Builder. I begin with adding a Grams method to integer.

image

I usually don’t show namespaces in my examples, but in this case it’s relevant—it means that the Grams method will only show up if I’m in the right namespace.

I return a quantity, which is simple illustration of the quantity pattern.

image

Although quantity is a class I’m writing, I don’t think the Of method belongs on it—because Of is part of a DSL for a limited purpose, while the quantity class can be used as part of a general library. So I use an extension method again.

image

The DSL code creates ingredient objects.

image

I use strings in the DSL to name the ingredients, resolving them to objects with a registry acting like a Symbol Table.

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.155.187