Skip to content
Switch branches/tags
Go to file
Cannot retrieve contributors at this time


From the program design standpoint, Brittany performes a Config -> Text -> Text transformation; it is not interactive in any way and it processes the whole input at once (no streaming going on). This makes for a very simple design with nice separation of IO and non-IO.

Brittany makes heavy usage of mtl-on-steroids-style transformers, mostly limited to Reader, Writer and State. For this kind of task it makes a lot of sense; we do a pure transformation involving multiple steps that each requires certain local state during traversals of recursive data structures. By using MultiRWS we can even entirely avoid using lens without inducing too much boilerplate.

Firstly, the topmost layer, the IO bits:

The corresponding code is in these modules:

  • Main
  • Language.Haskell.Brittany

The latter contains the code to run our Reader/Writer/State stack (well, no state yet).

Note that MultiRWS here behaves like a nicer version of a stack like ReaderT x (ReaderT y (WriterT w1 (WriterT2 w2 (Writer w3)..). The next graph zooms in on that transformation:

Two places (The BriDoc generation and the backend) have additional local state (added to the monadic context). The following is a very simplified description of the BriDoc generation:

For the BriDoc generation, the relevant modules are

  • Language.Haskell.Brittany.Layouters.*
  • Language.Haskell.Brittany.LayouterBasics

For the BriDoc tree transformations, the relevant modules are

  • Language.Haskell.Brittany.Transformations.*

Finally, for the backend, the relevant modules are

  • Language.Haskell.Brittany.Backend
  • Language.Haskell.Brittany.BackendUtils