Day 2: Taming Callbacks

In Day 2, we’re going to build the skills necessary to attack the most sophisticated of user interface problems: building a game. We’re going to learn to handle user input and output, the most difficult concepts for functional languages. We’ll also learn to display images. You’ll find that Elm is a natural language for doing so.

As a wanna-be browser language, Elm has a big disadvantage. It’s not JavaScript. You’ll need to rely on another layer in the browser to compile Elm to JavaScript. But Elm also has a huge advantage.

It’s not JavaScript.

If you want to herd sheep like a sheep dog, you don’t necessarily have to be a dog. You just have to herd sheep.

Before we get rolling, let’s spend some time with Evan Czaplicki, creator of Elm. He’ll help us understand the motivations behind the language.

Us:

Why did you create Elm?

Czaplicki:

I was extremely frustrated by HTML and CSS. Basic things like centering, or even worse vertical centering, were shockingly difficult. I kept finding five ways to do the same thing, each with its own set of weaknesses and corner cases. I wanted reusable styles and components. I was going to use the same sidebar on every page and there just was not a way. It makes sense why these things were hard in a language originally designed for text markup, but I felt that there had to be a more declarative and more pleasant way. So my goal was to create a better way to do GUI programming. I wanted to write front-end code that I was proud of.

Us:

So why choose a functional language?

Czaplicki:

I wanted to show that functional programming can be great for real problems. Many functional folks have a way of saying extremely interesting and useful things in a totally inaccessible and impractical way, and I wanted to fix this. I wanted to prove that functional programming actually helps you write nicer code. Elm’s focus on examples, quick visual feedback, and shockingly short code are all meant to prove that purely functional GUIs are a good idea.

Us:

What were your main influences?

Czaplicki:

Haskell has been a big influence, but so have OCaml, SML, and F#. Syntax is very much like Haskell, though semantics are often closer to OCaml. I tend to say “Elm is an ML-family language” to get at the shared heritage of all these languages.

Stephen Chong and Greg Morrisett are my major influences in how I think about programming languages. With that foundation, I try to do a literature review for any new feature and end up looking at all sorts of languages. For example, Java and Python were extremely helpful for Elm’s docs format, and Clojure and Scala are great resources on how to present a compile-to-VM language to people new to functional programming. The full list is quite long by now!

Us:

What is the philosophy of the language?

Czaplicki:

Balance simplicity and expressiveness. Introduce only the minimal set of features to make GUI programming a great experience. Static types, functional programming, and reactive programming are extremely important tools for writing short and reliable code, but it is a lot to learn all at once.

Not only does Elm need to make these things simple and accessible, it needs to make their value immediately obvious. Elm is not about being theoretically better, it is about being demonstrably better.

Us:

What is your favorite language feature?

Czaplicki:

I really love Elm’s extensible records. This feature is based on Daan Leijen’s ideas from Extensible Records with Scoped Labels, and because I was not involved in the theory work, it is something that delights me by balancing expressiveness and simplicity so beautifully. This is the kind of balance I hope to achieve when I design features.

Elm was built from the ground up to handle the most difficult aspects of user interface development. As you work through Day 2, look for ways that this new language helps you herd all of the elements of a great design into a coherent application.

Grappling with Callback Hell

Whether you’re building a business application with user interface controls or a game, you need to be able to react to events. In fact, everything you do is a reaction to some event. The typical JavaScript program relies on sending events through callback functions, making programs much more responsive but at a cost. They’re much too hard to read. Here’s a typical example using the JQuery library with JavaScript that lets you grab the mouse position:

 
$(document).ready(​function​ () {
 
var​ position = {'x': 0, 'y': 0};
 
$(document).bind(​'mousemove'​, ​function​(event) {
 
position = {'x': event.pageX, 'y': event.pageY};
 
});
 
 
setInterval(​function​ () {
 
// custom position code
 
}, seconds * 1000);
 
});

Understanding that code takes a little experience. When the page loads, we get a ready callback. At that time, we bind the mousemove event to a function that sets a position variable. Then, at specific intervals, we have another callback function that uses position. Notice that our code binds anonymous functions to events. Said another way, we’re putting JavaScript in charge of the code’s organization. We call this inside-out programming strategy inversion of control.

For a feature so trivial, that code is much too complex, but it’s a trade-off. We get better responsiveness since this program will change the mouse position every time the user moves the mouse. We trade away simplicity. The problem is that we really need both.

Avoiding Callbacks with Maps and Signals

In Elm, we don’t give up simplicity to get responsiveness. Instead of inversion of control, we’ll use signals. A signal represents I/O as a value that varies over time. Let’s try it out.

These programs will allow us to see how Elm handles user interaction without callbacks. For this part of the chapter, we’ll use the Elm online editor[59] to try interactive programs without having to fire up your own server. You’ll type code on the left, and see the results on the right. Let’s start with a simple function to pick up the user’s mouse position:

 
import​ ​Graphics​.​Element​ exposing (..)
 
import​ ​Mouse
 
 
main = ​Signal​.map show ​Mouse​.position

The exposing directive lets us show without specifying the module. Next, click the compile button. You’ll see output that looks like this:

 
(29, 162)

That’s much simpler. We import the Graphics.Element and Mouse modules, and then declare the main function.

Conceptually, the Mouse.position signal represents the values of x and y that vary over time. Signal.map applies a signal to a single function.

In the previous code, the Mouse.position signal represents a tuple containing the mouse position over time. Our function is show, which converts to text. Mouse.position will “fire” whenever the mouse moves, and Signal.map will reevaluate show with the new mouse position. Interestingly, the result is a new signal! Rather than a callback, you have straight composition of functions. The result is revolutionary.

Looking at the bottom of the window, you can see that main is actually a signal—one that we display on the screen. That means Elm will update the window whenever the mouse position moves.

There are no callbacks, and no inversion of control. We just use a signal, convert to text, and map the present value when the signal changes. Let’s try another one.

Maintaining State

Let’s use these same principles to produce an interactive experience. We’ll add a couple of functions to count the number of mouse interactions. In functional languages like Elm, you have to learn tricks to handle state. We’ve seen how signals can help access things like the mouse position that changes over time, and how we use recursion to process lists. We manage state by the way we structure our functions. The fold functions, which you might know from Lisp or Haskell, are a good example. They take a two-argument function, an initial value, and a list. Here’s an example of foldl in Elm:

 
> foldl (+) 0 [1, 2, 3]
 
6 : number

Here’s what happens at each step:

Now, we create one signal with foldp. That signal adds the accumulator, called presses, to the x value from the signal of Keyboard.arrows. We can then map that value onto the show function. Now, when you run the application, you’ll get a running total of presses. The left decrements the count, and the right increments the count.

Let’s use the same general principle to count mouse movements. Signal.map applies a signal to a function. Signal.foldp folds over a signal, from the past:

 
import​ ​Mouse
 
import​ ​Graphics​.​Element​ exposing (show)
 
 
count signal = ​Signal​.foldp (\_ n -> n + 1) 0 signal
 
main = ​Signal​.map show (count ​Mouse​.position)

Navigate to the window on the right, move the mouse, and you’ll see a number that quickly counts mouse moves:

 
246

The count function takes a signal and adds one each time that signal changes. foldp works just like foldl did, but instead of folding across a list from the left, foldp folds through time, from the past. Our foldp function takes an anonymous function that adds one to a value, starts with an initial value of 0, and provides a signal. Our new signal will have values starting with 0 that increase each time the signal updates.

We can easily change the program to count mouse clicks:

 
import​ ​Mouse
 
import​ ​Graphics​.​Element​ exposing (show)
 
 
main = ​Signal​.map show (count ​Mouse​.clicks)
 
count signal = ​Signal​.foldp (\_ n -> n + 1) 0 signal

In this case, the count function counts the number of signal updates, which are mouse clicks. You can start to see how we can write code that respects the rules of functional programming, but is still reactive and easy to understand.

Believe it or not, foldp is the foundation of our game, as you’ll see on Day 3.

Let’s see how keyboard signals would work:

 
import​ ​Graphics​.​Element​ exposing (show)
 
import​ ​Keyboard
 
 
main = ​Signal​.map show ​Keyboard​.arrows

Compile it, click on the right-hand window, and press the up and right arrows. You’ll see:

 
{ x = 1, y = -1 }

You can intuitively see exactly what’s going on. map updates the text when the signal changes, so we get a clean program that tells us the state of the arrow keys, in a form that we can easily use. Since we can compose with functions, we can get more sophisticated.

Combining Signals

Most user interfaces use more than one signal at once. For example:

These problems are all combinations of signals. For more advanced applications, a simple map is not enough. Several other functions help us combine signals in more sophisticated ways. One of the most common user interface problems is to find where a user clicks.

Let’s use the function sampleOn. That function allows us to sample one signal when another updates, like this:

 
import​ ​Graphics​.​Element​ exposing (show)
 
import​ ​Mouse
 
 
clickPosition = ​Signal​.sampleOn ​Mouse​.clicks ​Mouse​.position
 
main = ​Signal​.map show clickPosition

We build two signals, clickPosition and main. First, we create a signal with sampleOn. When the Mouse.Clicks signal updates, we’ll sample the most recent Mouse.position. The result is a new signal that returns a mouse position and changes whenever the user clicks a mouse. Next, we simply build our main signal. We map show onto our clickPosition signal. Simple. We can sample input controls in the same way.

Or, let’s say you’re implementing scrolling with a scroll bar. You need to find out how far down a page the mouse is, like this:

 
import​ ​Graphics​.​Element​ exposing (show)
 
import​ ​Mouse
 
import​ ​Window
 
 
div x y = show ((toFloat x) / (toFloat y))
 
main = ​Signal​.map2 (div) ​Mouse​.y ​Window​.height

Run it and scroll on the right-hand side to get something like this:

 
0.42973977695167286

This example uses map2. Like map, this function maps functions onto signals, but uses two signals and two-argument functions.

First, to simplify type conversions, we create a version of division that takes integers and returns text. Next, we use map2 to map div onto two signals, Mouse.y and Window.height. Think about what a similar JavaScript program would look like. It doesn’t take too many examples to see Evan’s vision. Monitoring user inputs is a functional job.

Working with Text Input

Functional languages are great at transforming text. Elm is excellent for capturing text too. Here’s an example that takes some input, manipulates it, and puts it on the screen, using an HTML flow:

​ 
import​ ​String
 
import​ ​Html​ exposing (​Html​, ​Attribute​, text, toElement, div, input)
 
import​ ​Html​.​Attributes​ exposing (..)
 
import​ ​Html​.​Events​ exposing (on, targetValue)
 
import​ ​Signal​ exposing (​Address​)
 
import​ ​StartApp​.​Simple​ as ​StartApp
 
​ 
main = ​StartApp​.start { model = ​""​, view = view, update = update }
 
​ 
update newStr oldStr = newStr
 
​ 
shout text = ​String​.toUpper text
 
whisper text = ​String​.toLower text
 
echo text = (shout text) ++ ​" "​ ++ (whisper text)
 
​ 
view address string =
 
div []
 
[ input
 
[ placeholder ​"Speak"
 
, value string
 
, on ​"input"​ targetValue (​Signal​.message address)
 
, myStyle
 
]
 
[]
 
, div [ myStyle ] [ text (echo string) ]
 
]
 
 
myStyle = style [ (​"width"​, ​"100%"​) ]

We import the libraries we’ll need. String allows us to do string manipulation, and Html gives us access to various aspects of HTML including events, divs, and input fields.

Our one-line main function starts our application, initializing with an empty string, presenting a view, and handling updates with our update function, which will be called each time our entry field changes.

Next, our trivial update function simply returns the new value of the entry field each time the entry field updates.

Next, we define a couple of simple functions for working with text, the shout and whisper functions. We use those to build an echo function to transform the text. These functions know nothing about user interfaces. They just work on raw String data.

The next task is to build our HTML page. We add an input control and a div that will contain our changed text. The input control simply has the HTML elements Elm needs to render the control. The on function establishes a signal that will contain updates of the entry field. myStyle returns the style sheet for our HTML elements. The div function creates an HTML div with the specified contents and style.

Whew. That’s a lot of code packed into a short example. It may seem a little alien at first, but Elm’s worldview is the perfect complement to web front-end programming. Each user interface is just a stream of transformed user inputs. Now that we’ve seen how text works, let’s look at one more concept we’re going to need for our game. Instead of working with text, we will draw shapes based on user input.

Drawing Shapes

images/src/elm/car.png

In Elm, we can draw on the canvas with a full graphics library. We start with a collage with set dimensions, and then build shapes. We can transform the shapes by moving, scaling, or rotating them.

The figure shows a simple car. We’ll describe it in terms of functions. As you’d expect, we’ll use a combination of data structures and functions to do what we want.

elm/car.elm
 
import​ ​Color​ exposing (..)
 
import​ ​Graphics​.​Collage​ exposing (..)
 
import​ ​Graphics​.​Element​ exposing (..)
 
 
carBottom = filled black (rect 160 50)
 
carTop = filled black (rect 100 60)
 
tire = filled red (circle 24)
 
 
main = collage 300 300
 
[ carBottom
 
, carTop |> moveY 30
 
, tire |> move (-40, -28)
 
, tire |> move ( 40, -28) ]

First, we define a few basic shapes. We’ll define the basic dimensions of the shapes, and by default they’ll show up in the middle of the canvas. main is just a collage, which takes a width, a height, and a list of shapes, called forms in Elm. Each element of the list is just a shape. For example, carTop |> moveY 30 is just a rectangle moved 30 pixels vertically.

In this particular example, the figure is static. With Elm, animating that figure is nearly trivial. Say we have a rectangle with a form that looks like this:

 
filled black (rect 80 10)

When we build our game on Day 3, we’ll need a paddle. We can animate the paddle by mapping Mouse.x onto the function that draws this paddle, like this:

elm/paddle.elm
 
import​ ​Color​ exposing (..)
 
import​ ​Graphics​.​Collage​ exposing (..)
 
import​ ​Graphics​.​Element​ exposing (..)
 
import​ ​Mouse
 
import​ ​Window
 
import​ ​Signal
 
 
drawPaddle w h x =
 
filled black (rect 80 10)
 
|> moveX (toFloat x - toFloat w / 2)
 
|> moveY (toFloat h * -0.45)
 
 
display (w, h) x = collage w h
 
[ drawPaddle w h x ]
 
 
main = ​Signal​.map2 display ​Window​.dimensions ​Mouse​.x

Boom! Just like that, we have animation. We don’t have to worry about drawing the paddle over time, or remember the previous location of the paddle. Instead, we just worry about drawing the paddle right now, and letting the user input determine where to move it. Now, you have all of the foundation you’ll need to complete a game in Day 3. Let’s recap.

What We Learned in Day 2

In Day 2, you learned about Elm’s primary purpose. Previous languages focused on callbacks or simply single-threaded code to create programs that respond to users. The cost was complexity or unresponsive interfaces. Functional programming languages have traditionally struggled with user interfaces because processing user input often involved changing state.

Elm solves both problems with signals, which are functions that represent values that change over time. By viewing user input as functions rather than data, the same beautiful functional programming techniques that express complex computations can be brought to bear on complex user interfaces.

We learned to transform signals by mapping functions onto them with map and map2. Each time, the result is a new signal. We also used other functions to combine signals and functions such as foldp, which maintains a running accumulator, and sampleOn, which determines exactly when we sample.

Finally, we displayed some text and graphics. We also mapped a display function onto a paddle, which will come in handy when we work on our game.

Your Turn

Use the online editor[60] to solve these problems interactively.

Find…

Do (Easy):

Do (Medium):

Do (Hard):

That’s it for Day 2. Tomorrow, you’re going to combine everything we’ve learned so far to write a game. We’re going to go far beyond pong in this one, so fasten your seatbelt.