Friday was my last day at Gamesys, bringing to an end a wonderful 6 years I’ve enjoyed at the company. Seeing as I’m starting my new job at JustEat in November, so for the first time in almost 10 years I have time on my hands to relax and go through my backlog of TODOs.
I wanted to try out the same examples in Elm, which is another hobby of mine.
Because it has this awesome time-travelling debugger, which I can use to see how the different examples grow frame by frame and then even go back and forth!
As the wikipedia page for L-System contains quite a few different examples, I’ll go through the implementation for each in Elm over a few posts.
To start nice and easy, let’s look at the Algae example.
Modelling a L-System
Since there’s no drawing involved, this example allows us to focus on modelling a L-System with types.
As you can see, my approach here is almost identical to Mathias’s, with the exception that Symbol is a type alias for Char rather than a union type of its own right.
Mostly because you can’t use a union type as key in a Dict in Elm. But also because I find it simplifies consuming code in this case, whilst a type alias still allows me to surface my domain through types.
Personally I’ve used both approaches and make my choice based on the chance of getting it wrong (or, what additional type safety buys me) and the amount of effort involved.
p.s. Scott Wlashcin wrote a nice piece about the 2 approaches from a performance point-of-view, which I also recommend you reading.
Then we just need a way to “evolve” a state based on the rules of a L-System:
Since Dict.get returns a Maybe, we have to provide a default value – the symbol itself in this case – if the symbol is not found in the rules.
To evolve the L-System to a given generation, we can just use simple recursion:
Modelling the Signals
For the demos, I want to be able to go from generation to generation using the arrows keys.
e.g. starting with gen 0, -> moves to gen 1, -> again moves to gen 2. But if you press <- then you move back to gen 1, and so on.
To do that, we need to capture the arrow key presses, and use the x value (see mapping below) to “change” the current generation.
Starting with gen 0 (the axiom), we’ll fold over the signal of integer values (either -1 for LEFT, or +1 for RIGHT), whilst capping the output value at 0 (hence the max 0 at the end).
Finally, we’ll map this signal of generation numbers into a signal of states by evolving the L-System to the specified generation:
All the code we’ve seen above are put inside a Core module that all the other examples will share.
Example 1 : Algae
So now we can finally look at one of the L-System examples. Once the Core module is in place, there’s actually not much to be done here.
We still need to define the L-System for Algae:
but since there’s nothing to be drawn on the screen, we’ll just keep things simple and display the output (list of Char) as a string instead:
Live Demo (here)
Remember, use LEFT and RIGHT arrow keys to evolve/devolve the L-System.
Source Code (here)
Next : Pythagoras Tree
- Fun with L-system (with F#)
- L-System wikipedia
- Elm’s online editor
- Source code for this post
- Live demo
I specialise in rapidly transitioning teams to serverless and building production-ready services on AWS.
Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help.
Check out my new course, Complete Guide to AWS Step Functions. In this course, we’ll cover everything you need to know to use AWS Step Functions service effectively. Including basic concepts, HTTP and event triggers, activities, callbacks, nested workflows, design patterns and best practices.
Here is a complete list of all my posts on serverless and AWS Lambda. In the meantime, here are a few of my most popular blog posts.
- Lambda optimization tip – enable HTTP keep-alive
- You are thinking about serverless costs all wrong
- Many faced threats to Serverless security
- We can do better than percentile latencies
- I’m afraid you’re thinking about AWS Lambda cold starts all wrong
- Yubl’s road to Serverless
- AWS Lambda – should you have few monolithic functions or many single-purposed functions?
- AWS Lambda – compare coldstart time with different languages, memory and code sizes
- Guys, we’re doing pagination wrong