# Writing functional DSLs for business domains

In functional programming, a domain specific language (DSL) is a set of functions that can be composed to solve a specific problem. They are often found in libraries, but you can also write your own DSL that is specific to your business domain. This can be beneficial for several reasons:

- Testable — Each independent component is small and isolated;
- Understandable — Composed solutions are easy to read;
- Expressive — Solve an entire class of problems with a small set of primitives.

In this post we’ll build a DSL for filtering emails in Scala. When we’re done, we can compose any email filter using simple, orthogonal building blocks.

An example of a DSL for the domain of email filtering

**Anatomy of a functional DSL**

A functional DSL consists of three components:

*Types*that describe solutions;*Constructors*for those types, that give the simplest possible solutions;*Operators*that compose or transform solutions.

With these components, we can construct a solution for a business domain. They are encoded as pure data, which can be evaluated to get a result.

**Types**

There should be a single type that describes a solution to a domain problem. In our case that’s the `EmailFilter`

trait, that describes solutions to the domain problem of filtering emails. Use `sealed trait`

s, `case class`

es and `case object`

s for this.

**Constructors**

The constructors for these types create the simplest possible solutions. For example, `bodyContains`

or `recipientIn`

. Constructors that use only one case class are called primitive constructors. Derived constructors, like `senderIsNot`

, use a combination of constructors and operators.

**Operators**

Operators can combine and transform the data structures in order to Lego together more complex solutions. Like constructors, operators can be either primitive, like `negate`

, or derived, like `||`

.

**Design principles**

There are many ways to factor a DSL, but some are better than others. These guiding principles help come to a good design. Our components should be

- Composable — to build complex solutions using simple components;
- Orthogonal — such that there’s no overlap in capabilities between primitives (i.e. MECE or the single-responsibility principle);
- Minimal — in terms of the number of primitives.

As always, it takes iteration and refinement to converge to a clean DSL. For example, consider `List`

's `flatMap`

, `flatten`

, and `map`

functions. We could implement `flatMap`

and derive the other two.

Or we could implement `flatten`

and `map`

, and derive `flatMap`

.

So which one is better? They are equally composable, because they result in the same operations. The first approach is more minimal, because it has only one primitive. And the latter is more orthogonal, because `flatten`

and `map`

can’t be split up into smaller operations, but `flatMap`

can. When deciding between minimalism and orthogonality, go with orthogonality, because that gives the simplest design.

**Evaluating the DSL**

So far we’ve only defined a way to *build* a data structure. There are two approaches for evaluating it: final and initial encoding.

**Final encoding**

This approach embeds the evaluation code in the data structure itself as it’s constructed. You can think of final encoding as describing a process of steps that should be executed. The resulting data structure will be executable.

Final encoding can be more straightforward to implement and allows wrapping existing code like libraries, that you might not be able to change.

**Initial encoding**

Initial encoding completely separates the evaluation from the data. There are one or more interpreters that traverse the data structure. The `run`

function in this example evaluates if a given email matches the filter. We could also define interpreters that generates a human-readable string for the filter, persist it in a database, simplifies or optimizes it. This can’t be done with final encoding, because functions are opaque — they can’t be inspected.

Simplified for readability. There’s a link to the full implementation below.

Because evaluation is separated from the data, it’s simpler to reason about and gives more flexibility than final encoding. So even though there’s more boilerplate code involved, initial encoding is usually preferred in green field scenarios.

**Putting it all together**

And that’s all we need in order to write a DSL for a specific business domain: constructors for types that describe solutions, operators to compose solutions, and a way to evaluate the solutions.

You can read through the full email filtering example for both initial encoding and final encoding.

*Thanks to* *John de Goes* *whose* *functional design workshop* *was the inspiration for this post.*