Elm – functional reactive dreams + missile command

I saw this tweet on my time­line the oth­er day..

image

which remind­ed me again to look at Elm and I’ve spend the last week or so get­ting myself immersed with this won­der­ful lit­tle lan­guage built around the idea of func­tion­al reac­tive pro­gram­ming.

My first impres­sions of Elm so far have been very pos­i­tive, there are some gen­uine­ly inter­est­ing things here, such as its Record and Sig­nal types and its inter­ac­tive debug­ger which is heav­i­ly influ­enced by Bret Victor’s work.

 

The Basics

Syn­tac­ti­cal­ly, Elm looks like many func­tion­al lan­guage (and Python) out there – no curly brack­ets, white­space mat­ters, etc. – which is per­haps unsur­pris­ing as there’s seems to be a strong Haskell influ­ence here.

 

Tuples

Tuples are enclosed in paren­the­ses and sep­a­rat­ed by com­ma (,) – e.g. ( 4, “3” ) is a tuple of the inte­ger 4 and the string “3”.

You can also use the helper func­tions (,) („) („,) … etc. to con­struct tuples with the required num­ber of items.

image

 

Records

At first glance Elm’s record type looks very sim­i­lar to F#’s record type:

  • both are light­weight labelled data struc­ture
  • both sup­port pat­tern match­ing
  • fields are immutable (but F# sup­ports option­al muta­bil­i­ty)
  • both sup­port copy-and-update seman­tics to cre­ate a new record based on an exist­ing record
  • both sup­port poly­mor­phic func­tions (though in F# it’s more idiomat­ic to use mem­bers instead)

How­ev­er, upon clos­er inspec­tion you find some inter­est­ing addi­tions with Elm, most notably:

  • exten­si­bil­i­ty – besides the copy-and-update seman­tic, you can also add and remove fields at the same time
  • records uses struc­tur­al typ­ing – a func­tion can accept records of any kind so long they have the required fields (e.g. whoAreY­ou { name } = … takes any record that has a name field)
  • record type alias­es can be com­posed togeth­er

 

Have a look at my last post to see a more detailed run-down of the sim­i­lar­i­ties and dif­fer­ences between records in F# and Elm.

 

Alge­bra­ic Data Struc­ture

Elm sup­ports alge­bra­ic data struc­tures (i.e. F# dis­crim­i­nat­ed unions), there are a num­ber of built-in alge­bra­ic types such as the Maybe type. You can also define your own alge­bra­ic types using the data key­word.

image

 

F# Pipes

It’s also nice to see F#’s pipes make an appear­ance in Elm and that Elm’s cre­ators are recep­tive to adopt­ing F#’s func­tion­al com­po­si­tion oper­a­tors « and » too, if adopt­ed I real­ly think they can make code that com­pos­es func­tions more read­able and intu­itive.


image

 

Pat­tern Match­ing

Elm sup­ports the case..of syn­tax (anoth­er exam­ple of its Haskell her­itage) for pat­tern match­ing against vari­ables as you can see from the snip­pet above.

Elm doesn’t have when guards in its pat­tern match­ing syn­tax yet, but you can work around this by using the mul­ti-way if expres­sions for now, which actu­al­ly reads like pat­tern match claus­es rather than the tra­di­tion­al if-elif-elif-else.

image

Elm also sup­ports pat­tern match­ing against tuples, records and lists, etc. in much the same way as you’ve seen in F# or Haskell, for instance, you can pat­tern match against a record using the { fieldName1, fieldName2, … } syn­tax:

image

 

Anony­mous Func­tions

Elm uses the same syn­tax as Haskell to define anony­mous func­tions.

image

 

Cur­ry­ing

As you’d expect, Elm sup­ports cur­ry­ing.

image

The stan­dard library also has two helper func­tions – cur­ry and uncur­ry – to help you change how argu­ments are passed to a func­tion, which comes in handy some­times.

  • cur­ry : ( ( a, b ) –> c ) –> a –> b –> c
  • uncur­ry : ( a –> b –> c ) –> ( a, b ) –> c

 

let..in syn­tax

Like many oth­er func­tion­al lan­guages (Haskell, F#, etc.), in Elm the last expres­sion of a func­tion pro­vides its return val­ue and whilst F# has moved away from the let..in syn­tax from its own OCaml roots, Elm still uses the let..in syn­tax.

You can spec­i­fy mul­ti­ple vari­ables with­in one let clause, e.g.

image

And you can also define inner func­tions in the same way too:

image

 

JS Interop

Interop with Javascript is pro­vid­ed via ports, which I haven’t played around with much, but you can read all about it here.

 

The Good stuff

In Elm, much of what you do revolves around Sig­nals (which is Elm’s equiv­a­lent of Rx’s observ­ables) and the lan­guage (along with its stan­dard libraries) gives you a very dif­fer­ent way of think­ing about GUI devel­op­ment to the tra­di­tion­al DOM based approach­es. If you are famil­iar with Rx (or RxJs, the Javascript port of the Rx API) or func­tion­al reac­tive pro­gram­ming* in gen­er­al then you should feel right at home here.

Elm’s stan­dard library comes with a num­ber of built-in sig­nals – mouse posi­tions, clicks, win­dow dimen­sions, timer events, etc. From these built-in sig­nals you can then com­pose and map over them, the built-in Sig­nal library pro­vides quite a num­ber of func­tions to make life easy there.

 

Lift func­tions

You can use the lift func­tions (lift, lift2, lift3, … lift8) to apply a nor­mal func­tion over sig­nals. In Rx you’d do this with Observable.Select, but the way you can com­bine sig­nals togeth­er using the short­hand oper­a­tors <~ and ~ is real­ly quite ele­gant.

For instance, you can pass the sig­nals of your mouse posi­tion and the coor­di­nate of your clicks into a func­tion like this:

And here is what the code above gen­er­ates:

Pret­ty sim­ple, huh?

 

Merging/combining sig­nals

You can use the merge, merges or com­bine func­tions from the Sig­nal stan­dard library to con­cate­nate mul­ti­ple sig­nals of the same type into one uni­fied sig­nal. This is equiv­a­lent to Rx’s Observable.Concat oper­a­tion.

This comes handy when you have mul­ti­ple sources of inputs of the same kind, or when you use alge­bra­ic data type to help uni­fy input sig­nals of dif­fer­ent kinds.

Take the Mis­sile Com­mand game below for instance, the game loop fol­lows the sim­ple pat­tern of: action –> state tran­si­tion –> redraw updat­ed state. How­ev­er, there are three dif­fer­ent kinds of input that would trig­ger state tran­si­tion:

  1. frames – to draw the game at 50 FRS means every 20ms we need to move the mis­sile along a cer­tain dis­tance along its path as dic­tat­ed by its speed
  2. play­er actions – when the play­er clicks on the screen we should launch a new mis­sile towards the coor­di­nate where the user clicked
  3. ene­my mis­siles – every x sec­onds we need to launch a ran­dom num­ber of ene­my mis­siles

Each type of input has a dif­fer­ent set of para­me­ters asso­ci­at­ed with them, uni­fied by a sin­gle alge­bra­ic data type.

Sig­nals rep­re­sent­ing each of these input types can then be merged togeth­er with the merge func­tion and passed into the main game tran­si­tion log­ic.

 

In addi­tion, the Sig­nal library also pro­vides a set of use­ful func­tions for com­pos­ing sig­nals, includ­ing foldp, count, keepIf, dropIf, keep­When, and sam­pleOn. Again, all of these can be mapped direct­ly to Rx exten­sion meth­ods.

In fact, all the capa­bil­i­ties of work­ing with sig­nals I have seen in Elm can be found in Rx and by exten­sion, in C#, F#, Java, Javascript and Dart thanks to the wide­spread reach of the Rx API and the effort by the devel­op­er com­mu­ni­ty to adopt it.

How­ev­er, because Rx exist as a library in those lan­guages, it doesn’t force you to fun­da­men­tal­ly change the way you view UI devel­op­ment because you always have a way out when­ev­er things get uncom­fort­able (which should be often, if you’re chal­leng­ing your­self to go out­side your com­fort zone). In Elm, how­ev­er, there’s no oth­er way, so you have to think ‘reac­tive’ all the way through. From a prac­ti­cal point of view this might sound restric­tive, but there’s real­ly no bet­ter way to learn the func­tion­al reac­tive par­a­digm than to ful­ly immerse and com­mit your­self to it.

 

The Debug­ger

If you have been fol­low­ing Bret Vic­tor’s work in recent years (which amongst oth­ers have inspired projects such as Light­Table, Khan academy’s UI design as well as Elm’s online debug­ger), you might have had to repeat­ed­ly scrape pieces of your brain from the walls thanks to Bret’s super awe­some demos.

I rec­om­mend giv­ing this page a good read to see how it works and how Elm makes it pos­si­ble.

Here’s a short demo video to show its time-trav­el­ling capa­bil­i­ties in action.

 

The Not So Good stuff

  • The lan­guage is still very young so expect lots of break­ing changes and bro­ken exam­ples from around the inter­net as it evolves. Per­son­al­ly, I think this is a good thing, it’s bet­ter for the lan­guage to learn and evolve away from its ear­ly mis­takes than to for­ev­er live with the sins of its youth.
  • IDE tool­ing is lack­ing. Whilst the online edi­tor and debug­ger can pro­vide hints (i.e. func­tion sig­na­tures and a link to the online doc for the func­tion, see below) for func­tions and types from the stan­dard libraries, they don’t work on user-defined func­tions and types. You can also just use any text edi­tor such as Notepad++ or Sub­lime Text that gives you syn­tax high­light­ing (Haskell high­light­ing usu­al­ly works pret­ty well) but I usu­al­ly look for much more sup­port from my devel­op­ment envi­ron­ment and see­ing Bret Victor’s work only rais­es my expec­ta­tions and Elm’s online debug­ger is a decent first step.

image

  • If you’re devel­op­ing with the online debug­ger then be sure to save your changes reg­u­lar­ly, to a source con­trolled file prefer­ably. As the debug­ger exe­cutes your code it col­lects events along the way, which is how it enables this awe­some time-trav­el­ling capa­bil­i­ty to go back in time to see the effect of your changes as you make them. With the Auto-Update option turned on you get to see your changes reflect­ed right away which is real­ly nice, the down­side being that if it has col­lect­ed a fair num­ber of events it can hang/crash the brows­er and you lose your unsaved changes along the way. Hope­ful­ly that’s a painful les­son you won’t have to learn the hard way.
  • There are a fair amount of guid­ed tuto­ri­als for the basic things, but once you start to get into the intermediate/advanced top­ics you’re most­ly left to work out how to do things based on avail­able exam­ples that are bare­ly com­ment­ed and those auto-gen­er­at­ed docs for libraries real­ly don’t do the read­ers jus­tice. This is com­mon for new lan­guages, I have had the same expe­ri­ence with Dart too despite Dart being much more mature and have a big­ger com­mu­ni­ty around it (and not to men­tion hav­ing a com­pa­ny like Google behind it).

 

Missile Command

Final­ly, to aid my learn­ing process, I put togeth­er a sim­ple imple­men­ta­tion of “Mis­sile Com­mand” which was fun to do and helped give me a sense of direc­tion when try­ing to fig­ure out what I can and can­not do in Elm.

I have writ­ten this in a way that’s per­haps more ver­bose than the offi­cial exam­ples, with lots of type anno­ta­tions to make up for the lack of sup­port from the online editor/debugger. Even so, the source code came in at less than 250 lines, which is real­ly encour­ag­ing, imag­ine the lev­el of pro­duc­tiv­i­ty a more com­pe­tent Elm devel­op­er is able to achieve!

The source code is avail­able on Github here, you can copy the con­tent of main.elm into the Elm online edi­tor to try it out.

 

* on the top­ic of reac­tive pro­gram­ming, I came across this talk by Erik Mei­jer, the father of Rx, titled Dual­i­ty and the end of Reac­tive and it’s well worth a watch. With­out going into too much detail and rob­bing you of the joy of mak­ing your own dis­cov­er­ies and con­clu­sions from it, my key take­away from the talk came from Erik’s clos­ing remark:

Reac­tive is Dead, long live com­pos­ing side effects.

Now, I don’t think Erik is telling us to stop using Rx or the reac­tive par­a­digm that he has done so much to pop­u­lar­ize, but to shift our focus from the act of doing them (com­pos­ing signals/observables and then writ­ing code that reacts to them) to the why – which is to help us ratio­nal­ize side effects and com­pose them, and mak­ing the implic­it rela­tion­ships between side effects explic­it.

 

Related Reading

Erik Mei­jer – dual­i­ty and the end of reac­tive <- (must watch)

Elm’s time-trav­el­ling debug­ger

Elm’s sig­nals

Elm’s exten­si­ble records

Aca­d­e­m­ic paper – Exten­si­ble records with scoped labels

Mis­sile Com­mand (demo)

Mis­sile Com­mand (source code)

Con­trast­ing F# and Elm’s record types

F# – record types vs class­es

Dart – emu­lat­ing alge­bra­ic data type

Udi Dahan – Mak­ing roles explic­it

Bret Vic­tor – Invent­ing on Prin­ci­ple

Bret Vic­tor – Stop Draw­ing Dead Fish

Bret Vic­tor – Media for Think­ing the Unthink­able