# Domain Modelling with Haskell: Factoring Out Recursion

In the final part of the "Domain Modelling with Haskell" series we factor out recursion from the Project data type, and use Fixplate to traverse the tree and accumulate reports. Although a highly flexible and powerful technique, it should not be employed prematurely due to its more advanced nature.## Show Notes

Welcome to Haskell at Work, a screencast focused on practical Haskell programming. You’re watching the fourth and final part of Domain Modelling with Haskell.

In the last video, we used explicit recursion and the WriterT monad transformer to accumulate reports on all project levels. We had to add new slots to the `Project`

data structure to add the additional reporting information. In a way, we have a strange coupling going on, where the `Project`

module knows how the `Reporting`

module extends the data structure.

If you only have a couple of these cases in your project, I think you should keep it that way. On the other hand, if you do a lot of transformation and processing based on a core data structure, like our `Project`

data type, you might want to make it even more generic. Let’s explore one way of doing that.

### Factoring Out Recursion

Instead of factoring out specific slots in the `Project`

and `ProjectGroup`

data constructors, we will factor out the entire recursion of the data type. We will use a library called Fixplate, whose name is a combination of the term “fixed-point type,” and “Uniplate,” the name of a Haskell package for generic traversals and transformations.

To use Fixplate, and specifcally its fixed-point type, we need to factor out the recursion from our data type. We see that the `ProjectGroup`

constructor has a list of projects. We will not have that explicit recursion, and instead embed a field of type `f`

. This technique is also the basis for free monads. If you are interested, check out Gabrial Gonzalez blog post Why free monads matter.

```
data Project f
= Project ProjectId
Text
| ProjectGroup Text
[f]
deriving (Show, Eq, Functor, Foldable, Traversable)
```

Note that we can have the `ProjectId`

in the `Project`

constructor again.

We’ll rename `Project`

to `ProjectF`

, by convention, and define a type alias `Project`

for `Mu ProjectF`

.

```
data ProjectF f
= ...
type Project = Mu ProjectF
```

We also need to import `Mu`

and its `Fix`

constructor:

`import Data.Generics.Fixplate.Base (Mu (Fix))`

`Mu`

is the fixed point type. We can have a look in the REPL:

```
$ stack repl
*Project> import Data.Generics.Fixplate.Base
*Project Data.Generics.Fixplate.Base> :t Fix
Fix :: f (Mu f) -> Mu f
```

I’m not going to explain in great detail how this works in this quick screencast, so do have a look at Gonzalez’ blog post. But you can see that the `Fix`

constructor takes a functor `f`

, parameterized by the fixed point type `Mu`

itself, and returns a `Mu`

. So `Mu`

is responsible for recursively injecting `f`

into `f`

itself, wrapped by the `Mu`

type at each level.

I know, this is a mind bender at first.

We create two helpers for constructing projects and project groups in the fixed-point type.

```
project :: ProjectId -> Text -> Project
project p = Fix . Project p
projectGroup :: Text -> [Project] -> Project
projectGroup name = Fix . ProjectGroup name
```

### Calculating and Accumulating Reports

Now here comes the first kicker. We want to *annotate* each level in the tree with the corresponding report. We can do that using Fixplate’s `Attr`

type (or *attribute*.) First, we import `fold`

and the `Attr`

type.

```
import Data.Foldable (fold)
import Data.Generics.Fixplate.Base (Attr)
```

We define a type alias `ProjectReport`

.

`type ProjectReport = Attr ProjectF Report`

Then, we rewrite `calculateProjectReports`

to use a function called `synthetiseM`

.

`import Data.Generics.Fixplate.Attributes (synthetiseM)`

`synthetiseM`

traverses our data structure bottom-up, and uses the function we supply to wrap and annotate each project level with a `Report`

. The function we supply, `calc`

, calculates a report for leaf projects. Given a project group, and it folds the underlying reports into a single one.

```
calculateProjectReports :: Project -> IO ProjectReport
calculateProjectReports = synthetiseM calc
where
calc (Project p _) =
calculateReport <$> DB.getBudget p <*> DB.getTransactions p
calc (ProjectGroup _ reports) = pure (fold reports)
```

Notice how the second field of `ProjectGroup`

is a list of reports, the reports below the group that have already been calculated. This, and many other powerful transformations and traversals, are possible thanks to our data type having recursion factored out.

In the end, we get back the full tree, with it’s structure preserved, but with reports on all levels. Before we can try it out, we need to adapt the pretty printing code.

### Pretty-Printing with Fixplate

Another big win is that because the recursion is factored out, Fixplate can take care of building the pretty-printable tree for us.

`import Data.Generics.Fixplate.Base (Ann (Ann))`

All we need to do is to tell it how to print project nodes. `prettyResult`

is a function from a project annotated with a report, to a string. The `Ann`

constructor holds the project and the report. A single project is printed as its name, the project ID, and the report. A project group is printed as its name and its report.

```
prettyResult :: Ann ProjectF Report a -> String
prettyResult (Ann report project') =
case project' of
Project (ProjectId p) name ->
printf "%s (%d): %s" name p (prettyReport report)
ProjectGroup name _ ->
printf "%s: %s" name (prettyReport report)
```

The project group children are values of the `Hole`

type, which is used to mark the absence of something to print. Remember, we don’t have to do recursion explicitly.

### Trying It Out

We need to change our `Demo`

data as well. First, we import the `Draw`

module from Fixplate.

`import Data.Generics.Fixplate.Draw`

Then, we will use the helper functions to create projects and project groups. The order of fields has changed, so let’s fix that.

```
someProject :: Project
someProject = projectGroup "Sweden" [stockholm, göteborg, malmö]
where
stockholm = project 1 "Stockholm"
göteborg = project 2 "Gothenburg"
malmö = projectGroup "Malmö" [city, limhamn]
city = project 3 "Malmö City"
limhamn = project 4 "Limhamn"
```

Now let’s try it out!

We open the REPL, load the `Demo`

module, calculate a project tree of reports, and draw the tree of reports using `prettyResult`

.

```
$ stack repl
...> :l Demo
*Demo> :l Demo
*Demo> pr <- calculateProjectReports someProject
*Demo> drawTreeWith prettyResult pr
\-- Sweden: Budget: -16682.99, Net: -1996.02, difference: +14686.97
|-- Stockholm (1): Budget: -2275.58, Net: -1816.54, difference: +459.04
|-- Gothenburg (2): Budget: -7492.88, Net: +1354.37, difference: +8847.25
\-- Malmö: Budget: -6914.53, Net: -1533.85, difference: +5380.68
|-- Malmö City (3): Budget: -1436.63, Net: +567.36, difference: +2003.99
\-- Limhamn (4): Budget: -5477.90, Net: -2101.21, difference: +3376.69
```

Same nice results as before, but using a different technique.

### Summary

With Fixplate, we have raised the abstraction level, and made our project data type highly extensible and reusable. This does, however, come with a cost, at least in terms of cognitive load for programmers.

I want to warn you of picking this technique up prematurely. Make sure you and your colleagues are comfortable with explicit recursion and monad transformers before considering recursion schemes. I would be careful about introducing this in a work project. The tradeoff is one you have to make yourself. If you are working a lot with similar data structures, recursion schemes, or one of the Uniplate-like libraries, might be a good choice for you.

And that is all for today!

## Source Code

The source code for the full series is available at github.com/haskell-at-work/domain-modelling-with-haskell.