The first time I was exposed to the functional programming paradigm was in Introduction to Programming at university. We used Scheme, a LISP dialect, to explore the structure and interpretation of computer programs. The perspective on programming was rather different from the imperative programming styles I had encountered in BASIC, assembler, Pascal, and C. I thought the style had an elegance, a feeling of neat-ness that I enjoyed. Even though I moved towards the object-oriented field in the coming years, the attraction of the functional paradigm remained.

In recent years, functional programming has experienced a revival as big data analytics, the advent of GPU support for number crunching, and the rise of machine learning have shown its merits. When I discovered that there is a functional language for browser-side code, i.e. a compiler that turns a functional programming language called Elm into JavaScript, I was ready for some exploration.

It’s a train!

My example will be a train traveling happily along its tracks. In the imperative or object-oriented programming paradigms, you would probably dash ahead an model a Train class and a Track class along with the necessary attributes and functions. But in functional programming, the approach is different. It reflects the way mathematicians look at the world, and one important idea is that of immutability. Consider for example the number 9. It is not going to change anytime soon. Or consider a mathematical function like the square root. Its result depends on the number you put into it but not on the point in time you evaluate it. The square root of 9 will always be 3, no matter when you check. This concept of immutability is extremely powerful and mathematicians use it all the time when manipulating formulas. It is also the main difference to the object-oriented style of programming where every object has an identity and a state, which in most cases you can change over time. Not so in functional programming.

Our train is a real world object, although only simulated. It does change state over time. It moves: It changes its position on the track, it might move to another track, it can change its speed and direction, and even its composition as cars get attached and detached. In functional programming, we don’t talk about a train at all. Instead, we talk about its state at a given time. This state will then be the input to some function move, which returns an entirely different train state. We know that this is the new state of our train, but the program does not care. Its inner mathematician just shrugs their shoulder and says: “I have evaluated your function. Do with the result whatever you please.”

In this series of articles I will not focus on the installation of Elm, or how to embed code into a web page like this one. The Elm Guide does an excellent job of that. I will focus on building a tiny toy simulation in Elm instead, because I like trains and because I want to explore functional programming, not html/svg generation.

To define the train state in Elm, I create a record structure and give it a convenient name, a type alias in Elm terminology. By the way, Elm likes to have its code aligned in a somewhat unconventional way, with the comma in front. Also, single line comments are introduced with a double hyphen, --. That’s all just optics, I’ll bear with it.

type alias TrainState =
    { name : String -- Give the train a nice name.
    , length : Float -- Total length of the train in m.
    , speed : Float -- Speed of the train in m/s.
    , trackPosition : Float -- Distance from the start of the track in m.

If we want to simulate the train movement, we need a function that will map a train state (the current state) to a different train state, the future state. How far does the train move? It depends on the elapsed time, so the function will take that as an additional parameter. This is what the function declaration looks like in Elm:

move : Int -> TrainState -> TrainState -- This is the function declaration.

Translated into English: move is the name of a function that takes an Int as its parameter and returns a function that takes a TrainState as its parameter and returns a TrainState. Fortunately, we don’t have to worry about this complexity yet, although it will come in handy at a later time. If we provide both parameters to the function, Elm is smart enough to call both functions consecutively behind the scenes. For simplicity, let’s just say this is a function taking two parameters, the time and the current state, and returns the new state of a train.

This is the implementation of the function:

move millis state = -- Naming the two parameters.
    { state -- Shorthand for: Create another record that is exactly like state
        -- ... so we don't have to repeat all the elements that don't change,
        -- ... but with a different value for trackPosition.
        | trackPosition = state.trackPosition + state.speed * toFloat millis / 1000.0

There is a bit of Elm syntax that looks unfamiliar. The part in the curly braces is a shorthand way to create a new record that is the same as some other record, with a few variations. The function move with its two parameters millis (the elapsed time in milliseconds) and state (the previous state of the train) returns a new train state that is the same as before, except that the trackPosition of the new state will be calculated with the given formula.

Note again that this does not change the train state itself; it creates a new one. The Elm notation is just a convenient shorthand so that we don’t have to write out initializations for all the elements of the data structure that are the same. If you wanted, you could keep the old train state around, for example to implement an instant replay function in your simulator.

Try this in the Elm interactive interpreter:

> type alias TrainState =
|   { name : String
|   , length : Float
|   , speed : Float
|   , trackPosition : Float
|   }
> move : Int -> TrainState -> TrainState
| move millis previous =
|   { previous
|   | trackPosition = previous.trackPosition
|     + previous.speed * toFloat millis / 1000.0
|   }
> myTrain =
|   { name = "Happy Train"
|   , length = 30.0
|   , speed = 10.0
|   , trackPosition = 40.0 }
{ length = 30, name = "Happy Train", speed = 10, trackPosition = 40 }
    : { length : Float, name : String, speed : Float, trackPosition : Float }
> move 1000 myTrain
{ length = 30, name = "Happy Train", speed = 10, trackPosition = 50 }
    : TrainState
> myTrain
{ length = 30, name = "Happy Train", speed = 10, trackPosition = 40 }
    : { length : Float, name : String, speed : Float, trackPosition : Float }

If you look at the results of the two last calls, the result of the move has a new track position, according to time and speed, but the variable myTrain still holds the old, unchanged train state. In functional programming, things never change. But of course, if we wanted we could assign the new state to the name myTrain.


In the next article, I will explore how to represent a track layout and how to implement movement from one track to the next.

And here is a very simple visualization of the simulation written in Elm. If nothing is moving, you have missed the train. Just click the reset button.