## Introduction

I like using Brent Yorgey’s
diagrams^{1}
package to create images by writing
Haskell^{2}. Here’s a small example:

and the code to generate it:

```
hello :: Diagram B
hello = vsep 5
. zipWith letterRow [ "HELLO", "WORLD!" ]
$ L.tails cols
where letterRow ls = centerX . hsep 5 . zipWith letterDisc ls
letterDisc l c = letter l <> circle 10 # lw 2.5 # fc c
cols = cycle [ red, green, blue ]
letter c = stroke (textSVG [c] 20)
# fc yellow # lw 1.0 # lc yellow
```

In the example above graphics primitives `circle`

, `stroke`

, and
`textSVG`

are combined to make the final image. The combinators
include `hsep`

and `vsep`

, which take lists of elements and stack them
separated by a space, and the `<>`

operator which puts one diagram on
top of another.

There are lots of little modifiers e.g. `lw`

which sets the line
width: these are just functions, but the `#`

operator lets us write
the object being styled before the styling.

This article covers a few points which struck me as being particularly interesting, or I wanted to think about more carefully. All the information is included in the fine, official documentation:

the user manual

^{4};

## A principled package

Like Haskell the diagrams package has a strong theoretical underpinning. As an example, an important distinction is made between a location in space (a Point) and a displacement in space (a Vector). Although both can be represented by a coordinate tuple, they are very different animals:

It makes no sense to add a Point to a Point and get a Point, but it is perfectly natural to add a Vector to a Vector and get a Vector. You could also add a Vector to a Point and get another Point.

If you translate a Point it becomes a different Point; translating a Vector leaves it unchanged. If this seems odd it might help to think of a Vector as the displacement between two Points, both of which will move in the same way when translated.

You can’t turn a Vector into a Point unless you specify an Origin.

Some software conflates Points and Vectors, perhaps because they often have the same representation: the Haskell diagrams package doesn’t. If you think the distinction is worthwhile, then I think you’ll enjoy using diagrams; on the other hand, if you think it’s just pedantry I suspect you’ll be frustrated.

The official diagrams documentation has a helpful introduction to
vectors and points^{6} which
discusses all this in more detail.

### Silly games with Vectors and Points

The basic type of a two-dimensional vector is `V2 n`

where `n`

tells us the underlying scalar type e.g. `Double`

. You can
make such a vector in lots of ways:

```
*Main> V2 1.0 2.0
V2 1.0 2.0
*Main> r2 (3.0, 4.0)
V2 3.0 4.0
*Main> (5.0 ^& 6.0) :: V2 Double
V2 5.0 6.0
```

You can make Points in similar ways, though note that there’s no
`P2`

constructor:

```
*Main> p2 (1.0, 2.0)
P (V2 1.0 2.0)
*Main> (3.0 ^& 4.0) :: P2 Double
P (V2 3.0 4.0)
```

Although it’s an internal detail, we make a Point by wrapping a Vector. For example we could have written the last example above as:

```
*Main> (3.0 ^& 4.0) :: Point V2 Double
P (V2 3.0 4.0)
```

Having created Vectors and Points, we can now transform them. Here we translate a Vector and a Point, noting that the former is unchanged:

```
*Main> translateX 10 $ r2 (0,1)
V2 0 1
*Main> translateX 10 $ p2 (0,1)
P (V2 10 1)
```

As you might expect, we can do all this in three-dimensions too e.g.:

```
> (0.0 ^& 1.0 ^& 2.0) :: V3 Double
V3 0.0 1.0 2.0
*Main> translateX 10 $ p3 (0,1,2)
P (V3 10 1 2)
```

### Polymorphism

The astute reader will have noticed that we applied `translateX`

to
both Points and Vectors, in both two- and three-dimensions. Clearly it’s
a polymorphic function so let’s look at its type:

```
*Main> :t translateX
translateX
:: (Additive (V t), Num (N t), R1 (V t), Transformable t) =>
N t -> t -> t
```

This rather scary signature needs a bit of unpicking. Ignoring the stuff before the fat arrow, we have the type:

`N t -> t -> t`

Having seen how it’s used, we know that `t`

is something like a Point
or a Vector, and `N t`

is a scalar of the appropriate type. In the
examples above, we had e.g.:

```
t ~> V2 Double
N t ~> Double
```

So it’s clear that `N`

is a type level function which extracts the
underlying type from a more complicated thing. Looking now at the
constraints before the fat arrow, we also see `V t`

which is the
vector-space in which `t`

lives.

Most of the constraints on `t`

are straight-forward: it needs
to be transformable, the underlying type has to be numeric, and
so on. The most interesting term is `R1 (V t)`

which loosely
means that the vector-space in which `t`

lives has to have a
first dimension: `R1`

extracts that coordinate.

By contrast if we look at `translateZ`

,

```
*Mail> :t translateZ
translateZ
:: (Additive (V t), Num (N t), R3 (V t), Transformable t) =>
N t -> t -> t
```

the `R1`

constraint is now `R3`

which constrains the vector-space
to have a third-dimension. In practical terms this means that if we
try to translate a two-dimensional point in the Z-direction, it will
fail at compile time:

```
*Main> translateZ 10 $ p2 (0,1)
<interactive>:66:1: error:
• Could not deduce (R3 V2) arising from a use of ‘translateZ’
from the context: Num n
bound by the inferred type of it :: Num n => P2 n
at <interactive>:66:1-24
• In the expression: translateZ 10
In the expression: translateZ 10 $ p2 (0, 1)
In an equation for ‘it’: it = translateZ 10 $ p2 (0, 1)
```

The meaning of this might not be immediately obvious to the casual observer.

## Type classes

It’s worth stating explicitly that many of the functions in the
diagrams API don’t take a particular type: rather they take any type
which conforms to the relevant type class constraints. This is elegant
and powerful, but it can lead to unwieldy signatures and Byzantine
error messages. The User Manual has some useful tips and tricks^{7} on this topic.

More positively, if we return to the `Transformable`

type class
above, we can find many
instances^{8}.
Unsurprisingly you can apply `translateX`

to all sorts of things,
including diagrams and other transformations. It’s nice that one
function can move so many things.

As with the translation examples above, particular transformations may place other contraints on the objects which are being transformed. However any type will work if it has the necessary instances to satisfy the constraints.

The diagrams manual has a good Type class
reference^{9}
which explains all this and more.

## Monoids

A general theme in Haskell is that abstract mathematical ideas are often translated into a Haskell type class. If you create something which obeys the laws of the type class, you can make an instance of the type class which both saves writing code and unifies syntax.

For example, a monoid^{10} is a
structure with a single associative operation and an identity
element. Essentially this means that we can take two things and
combine them into another thing of the same type, and that there’s a
particular element which doesn’t change things when you combine with
it. If there isn’t such an identity element you formally have a semigroup,
not a monoid, but I’ll gloss over that distinction here.

The Haskell type class corresponding to a monoid is
Data.Monoid^{11},
and a while ago I wrote some notes^{12} about
it. Rather than rehashing that theory, let’s just look at some
examples to illustrate the general idea.

You can make a list monoid where the operation is concatenation, and the identity element the empty list:

```
[1,2,3] <> [4,5,6] = [1,2,3,4,5,6]
[1,2,3] <> [] = [1,2,3]
[] <> [1,2,3] = [1,2,3]
```

It’s easy to see that this is associative:

```
([1,2] <> [3,4]) <> [5,6] = [1,2,3,4] <> [5,6]
= [1,2,3,4,5,6]
[1,2] <> ([3,4] <> [5,6]) = [1,2] <> [3,4,5,6]
= [1,2,3,4,5,6]
```

We could also make a monoid from the integers under addition with zero as the identity (or a different one under multiplication):

```
1 <> 2 = 3
1 <> 0 = 1
0 <> 1 = 1
(1 <> 2) <> 3 = 3 <> 3
= 6
1 <> (2 <> 3) = 1 <> 5
= 6
```

Although the meaning of the `<>`

operator changes, it obeys the same rules
in both cases.

Similarly, we can make a monoid for diagrams. Here, the operator means putting one diagram on top of the other:

I think it’s clear that the empty diagram is a perfectly good identity element here.

Turning back to the operator, the order matters, as it does with lists. Mathematically, we’d say that the operator isn’t commutative:

However, the operator is associative and that’s all that matters if you want to be a monoid:

### More diagrammatic Monoids

Besides diagrams themselves, the diagram package has many other monoid instances.

For example, if you have two transformations you can either apply them sequentially, or combine them into one uber-transformation and then apply that. So we can make a monoid instance for transformations.

Other examples abound: the word ‘Monoid’ appears nearly fifty times
in the documentation for `Diagrams.Core`

^{13}.

## Making a `#`

of things

Diagrams makes extensive use of `#`

which is flipped function
application. `fc red`

is a function which makes the foreground-colour
of a diagram red. You might use it thus:

` fc red (circle 2)`

but it is more elegant to say:

` circle 2 # fc red`

It’s worth emphasizing that there’s nothing diagrams specific about
`#`

. You could also say things like:

```
> "Wibble" # length
6
```

The `&`

function in `Data.Function`

is similar, but has a lower
precedence (1 vs 8). If we used this instead, we would typically
need more parentheses, and we’re writing Haskell not Lisp.

## Named diagrams

Diagrams (including subdiagrams) can be named, and then subsequently referred to by name. This is extremely helpful because it allows you to refer to some element of a diagram after it’s been composed.

For me it greatly extended the scope of the diagrams I could make
without using explicit coordinates^{14}.
)

There are many ways to use names, but for simple cases I use
`named`

^{15}
to give something a name:

`circle 1 # fc green # named "Foo"`

and `withName`

^{16}
to operate on a named subdiagram:

```
addMark n = withName n $
atop . place (circle 0.1 # fc red) . location
```

Names don't have to be strings: you can use any instance of
`isName`

^{17}.

### Chess board example

To show how useful names can be, consider the example below which draws a chess board with named squares, then fills it with pieces.

```
data Square = Square Char Int
deriving (Eq, Ord, Show)
instance IsName Square
chessBoard :: PreparedFont Double -> Diagram B
chessBoard f = L.foldl' (flip draw) cbBoard piecePosns
where draw (n,s) = withName n $
atop . place (myText f 16 [s]) . location
piecePosns :: [(Square, Char)]
piecePosns = concatMap (uncurry doFile) [('a', "♖♘♗♕♔♗♘♖")
,('b', "♙♙♙♙♙♙♙♙")
,('g', "♟♟♟♟♟♟♟♟")
,('h', "♜♞♝♛♚♝♞♜")
]
where doFile file = zipWith (\r p -> (Square file r, p)) [1..8]
cbBoard :: Diagram B
cbBoard = vcat . reverse
. zipWith cbRank ['a' .. 'h' ]
$ L.tails bgs
where cbRank rank = hcat . zipWith (cbCell rank) [1..8]
cbCell rank file b = square 10.0
# fc b # lw 0.5 # lc black
# named (Square rank file)
bgs = cycle [darkgoldenrod,lightgoldenrodyellow]
myText :: PreparedFont Double -> Double -> String -> Diagram B
myText f h t = stroke (textSVG' opts t)
# fc black # lw 0
where opts = TextOpts f INSIDE_H KERN False h h
```

### Font Acknowledgment

I should begin by saying that I’m using Alexander Lange’s fine Quivira
font^{18} to draw all the pieces, which makes
things much easier. If you want to use this:

download the font;

convert it into SVG format with FontForge

^{19};load the font with the SVGFonts package

^{20}.

### Implementation

The key function is `cbBoard`

which draws an empty board by assembling
squares into ranks, then ranks into the board. The cells are all named
with their rank and file e.g. `Square 'c' 7`

. It is nice that you
can use almost anything sensible as a name with relatively little
effort.

Having generated the board, we just fold over a list of pieces and their locations, grab the cell by its name and draw the piece on it. At no stage do we have to worry about where the cell is: we just ask for it by name.

## Conclusions

In many ways I think the diagrams package is a microcosm of Haskell itself: there’s quite a steep learning curve, because it embraces some clever and abstract ideas. However, once you’ve absorbed those ideas it’s a joy to use and affords new insights into the problem you’re trying to solve.

## References

- 1. https://archives.haskell.org/projects.haskell.org/diagrams/
- 2. https://www.haskell.org
- 3. https://archives.haskell.org/projects.haskell.org/diagrams/doc/quickstart.html
- 4. https://archives.haskell.org/projects.haskell.org/diagrams/doc/manual.html
- 5. http://hackage.haskell.org/package/diagrams
- 6. https://diagrams.github.io/doc/vector.html
- 7. https://archives.haskell.org/projects.haskell.org/diagrams/doc/manual.html#tips-and-tricks
- 8. https://archives.haskell.org/projects.haskell.org/diagrams/haddock/diagrams-core/Diagrams-Core-Transform.html#g:4
- 9. https://diagrams.github.io/doc/manual.html#type-reference
- 10. https://en.wikipedia.org/wiki/Monoid
- 11. http://hackage.haskell.org/package/base-4.14.0.0/docs/Data-Monoid.html
- 12. ../../2015/04/monoid.html
- 13. https://hackage.haskell.org/package/diagrams-core-1.4.2/docs/Diagrams-Core.html
- 14. https://archives.haskell.org/projects.haskell.org/diagrams/doc/manual.html?ref#using-absolute-coordinates
- 15. http://hackage.haskell.org/package/diagrams-lib-1.4.3/docs/Diagrams-Names.html#v:named
- 16. http://hackage.haskell.org/package/diagrams-lib-1.4.3/docs/Diagrams-Names.html#v:withName
- 17. http://hackage.haskell.org/package/diagrams-lib-1.4.3/docs/Diagrams-Names.html#t:IsName``
- 18. http://www.quivira-font.com
- 19. https://fontforge.org/en-US/
- 20. https://hackage.haskell.org/package/SVGFonts