Lsystems are a formal way to make interesting visualisations. You can use them to model a wide variety of objects: spacefilling curves, fractals, biological systems, tilings, etc.
See the Github repo: https://github.com/dlozeve/lsystems
What is an Lsystem?
A few examples to get started
Definition

An Lsystem is a set of
rewriting rules generating sequences of symbols. Formally, an Lsystem
is a triplet of:
 an alphabet $V$ (an arbitrary set of symbols)
 an axiom $\omega$, which is a nonempty word of the alphabet ($\omega \in V^+$)
 a set of rewriting rules (or productions) $P$, each mapping a symbol to a word: $P \subset V \times V^*$. Symbols not present in $P$ are assumed to be mapped to themselves.
During an iteration, the algorithm takes each symbol in the current word and replaces it by the value in its rewriting rule. Not that the output of the rewriting rule can be absolutely anything in $V^*$, including the empty word! (So yes, you can generate symbols just to delete them afterwards.)
At this point, an Lsystem is nothing more than a way to generate very long strings of characters. In order to get something useful out of this, we have to give them meaning.
Drawing instructions and representation
Our objective is to draw the output of the Lsystem in order to visually inspect the output. The most common way is to interpret the output as a sequence of instruction for a LOGOlike drawing turtle. For instance, a simple alphabet consisting only in the symbols $F$, $+$, and $$ could represent the instructions "move forward", "turn right by 90°", and "turn left by 90°" respectively.

Thus, we add new components to our definition of Lsystems:
 a set of instructions, $I$. These are limited by the capabilities of our imagined turtle, so we can assume that they are the same for every Lsystem we will consider:
Forward
makes the turtle draw a straight segment.TurnLeft
andTurnRight
makes the turtle turn on itself by a
given angle.
Push
andPop
allow the turtle to store and retrieve its
position on a stack. This will allow for branching in the turtle's
path.
Stay
, which orders the turtle to do nothing. a distance $d \in \mathbb{R_+}$, i.e. how long should each forward segment should be.
 an angle $\theta$ used for rotation.
 a set of representation rules $R \subset V \times I$. As before, they will match a symbol to an instruction. Symbols not matched by any rule will be associated to
Stay
.
Finally, our complete Lsystem, representable by a turtle with capabilities $I$, can be defined as \[ L = (V, \omega, P, d, \theta, R). \]
One could argue that the representation is not part of the Lsystem, and that the same Lsystem could be represented differently by changing the representation rules. However, in our setting, we won't observe the Lsystem other than by displaying it, so we might as well consider that two systems differing only by their representation rules are different systems altogether.
Implementation details
The LSystem
data type
The mathematical definition above translate almost immediately in a Haskell data type:
  Lsystem data type
data LSystem a = LSystem
{ name :: String
, alphabet :: [a]  ^ variables and constants used by the system
, axiom :: [a]  ^ initial state of the system
, rules :: [(a, [a])]  ^ production rules defining how each
 variable can be replaced by a sequence of
 variables and constants
, angle :: Float  ^ angle used for the representation
, distance :: Float  ^ distance of each segment in the representation
, representation :: [(a, Instruction)]  ^ representation rules
 defining how each variable
 and constant should be
 represented
} deriving (Eq, Show, Generic)
Here, a
is the type of the literal in the alphabet. For all
practical purposes, it will almost always be Char
.
Instruction
is just a sum type over all possible instructions listed
above.
Iterating and representing
From here, generating Lsystems and iterating is straightforward. We
iterate recursively by looking up each symbol in rules
and replacing
it by its expansion. We then transform the result to a list of Instruction
.
Drawing
The only remaining thing is to implement the virtual turtle which will
actually execute the instructions. It goes through the list of
instructions, building a sequence of points and maintaining an
internal state (position, angle, stack). The stack is used when Push~
and ~Pop
operations are met. In this case, the turtle builds a
separate line starting from its current position.
The final output is a set of lines, each being a simple sequence of
points. All relevant data types are provided by the
Gloss library, along
with the function that can display the resulting Picture
.
Common file format for Lsystems
In order to define new Lsystems quickly and easily, it is necessary to encode them in some form. We chose to represent them as JSON values.
Here is an example for the Gosper curve:
{
"name": "gosper",
"alphabet": "AB+",
"axiom": "A",
"rules": [
["A", "ABB+A++AA+B"],
["B", "+ABBBA++A+B"]
],
"angle": 60.0,
"distance": 10.0,
"representation": [
["A", "Forward"],
["B", "Forward"],
["+", "TurnRight"],
["", "TurnLeft"]
]
}
Using this format, it is easy to define new Lsystems (along with how
they should be represented). This is translated nearly automatically
to the LSystem
data type using
Aeson.
Variations on Lsystems
We can widen the possibilities of Lsystems in various ways. Lsystems are in effect deterministic contextfree grammars.
By allowing multiple rewriting rules for each symbol with probabilities, we can extend the model to probabilistic contextfree grammars.
We can also have replacement rules not for a single symbol, but for a subsequence of them, thus effectively taking into account their neighbours (contextsensitive grammars). This seems very close to 1D cellular automata.
Finally, Lsystems could also have a 3D representation (for instance spacefilling curves in 3 dimensions).
Usage notes
 Clone the repository:
git clone https://github.com/dlozeve/lsystems
 Build:
stack build
 Execute
stack exec lsystemsexe  examples/penroseP3.json
to see the list of options  (Optional) Run tests and build documentation:
stack test haddock
Usage: stack exec lsystemsexe  help
lsystems  Generate Lsystems
Usage: lsystemsexe FILENAME [niterations N] [ccolor R,G,B]
[wwhitebackground]
Generate and draw an Lsystem
Available options:
FILENAME JSON file specifying an Lsystem
n,iterations N Number of iterations (default: 5)
c,color R,G,B Foreground color RGBA
(0255) (default: RGBA 1.0 1.0 1.0 1.0)
w,whitebackground Use a white background
h,help Show this help text
Apart from the selection of the input JSON file, you can adjust the number of iterations and the colors.
stack exec lsystemsexe  examples/levyC.json n 12 c 0,255,255
References
 Prusinkiewicz, Przemyslaw; Lindenmayer, Aristid (1990). The Algorithmic Beauty of Plants. SpringerVerlag. ISBN 9780387972978. http://algorithmicbotany.org/papers/#abop
 Weisstein, Eric W. "Lindenmayer System." From MathWorldA Wolfram Web Resource. http://mathworld.wolfram.com/LindenmayerSystem.html
 Corte, Leo. "Lsystems and Penrose P3 in Inkscape." The Brick in the Sky. https://thebrickinthesky.wordpress.com/2013/03/17/lsystemsandpenrosep3ininkscape/