Converting an Elm app to Typescript, RxJs and Angular 2 — is there a clear winner?
As people are often interested in the Typescript and RxJs version of my Elm app (all code from my FullStack 2016 Haskell/Elm talk, see this post for resources) it seems helpful to write something on where to locate that code and add some comments on its purpose in the talk. Note that this is a brief guide to some key points of the code and not a full explanation.
Browse the code
The quickest way to view the Typescript and RxJs code is to view the source in the online app, with Chrome devTools (or your preferred browser). Although Typescript runs as transpiled code, the magic of source-maps means that the browser happily displays the .ts files and supports breakpoints and debugging with them.
Clone the code
The repo for the code is here: https://github.com/jkbits1/HaskellElmTalk/tree/master/rxjs/src
Relevant files and comments — Typescript
Of the three .ts files, only two are directly relevant here. The main body of the app (app.ts) employs a module defined in wheelCalcs.ts. That module contains the types plus related algorithm functions. The functions are defined and exported in class Calcs1. The Elm types equivalents are defined directly above that class.
Relevant files and comments — RxJs
The Rx code is in app.ts. Angular2 has been in flux prior to release and the previous use of EventEmitter in this app to react to input changes is now deprecated. It is replaced here by RxSubject, which works as a direct replacement. Usefully, RxSubject has the asObservable() method so subscribers see an observable only, not the subject itself.
RxSubject is useful when we want an object that, as a form of proxy, can both observe data itself and be subscribed to by one or more observers. One of the subjects here is a BehaviorSubject, which provides an initial value to subscribers. In Rx terms, this makes BehaviorSubject a “hot” observable and the others “cold”. The initial value triggers the first use of the algorithm and so displays the initial results.
An alternative approach to using RxSubject is to import Angular2’s ReactiveFormsModule and apply the formControl directive to specific fields. These fields then become observables directly. The subscribe code is the same for both approaches.
Our subscribe code is fairly straightforward. We perform a standard debounce, and only react to actual changes in the input. However, now that we have our data as Rx streams, it would be very simple to merge and filter data from fields or make changes according to values of multiple fields etc. The code would remain equally declarative.
this.updateModel(this, results, wheelPos),
updateModel() is the equivalent of that function in the Elm app. It receives the changed input and recalculates the algorithm, which causes the view/UI to be updated. In this way, the app follows the uni-directional pattern of the Elm app and overall its behaviour is defined in a declarative style.
The wheelCalcs.ts code above is a conversion of the Elm PuzzleModule. You can view the online Elm app (no source-mapping for Elm, though). Click the Rotate buttons to experiment with the D3 puzzle wheel and notice how the input fields adjust automatically. Elm’s inherent uni-directional style makes it simple to have input fields, information items etc., work in harmony.
Thoughts on Rx compared to Elm
I really like the power and expressiveness of Rx. It enables declarative programming for a wide range of languages and environments, of which we only see a small taste in this example. Elm has definitely shown that it creates resilient browser apps that perform as planned, and this is no small feat! However, while Elm 0.17 has made the language more accessible, it has moved away from its FRP beginnings. This has made me wonder how easy it remains to build various forms of Rx-style functionality in Elm. In future, perhaps I’ll need to make a decision on whether projects are best suited to one or the other of the technologies rather than both. I plan to do some tests, and may write a post on this sometime.
Hopefully, this has given you a brief glimpse of the benefits of TS, Rx and the uni-directional approach and how it might relate to your own projects. As before, feedback is very welcome.