Software is hard
Software is hard

WebApps with PureScript & RactiveJS

30 minutes read

It’s been a while since I wrote my last article and. Actually, I planned to write the second part of my Scala Crash Course but in the mean time I adopted an orphaned (abandoned?) project called purescript-ractive that’s related to my favorite UI-Library, RactiveJS, and is written in a little, amazing language called PureScript. As many of you already know there are so many different languages that compile to JavaScript. There’s even a COBOL transpiler for JS! Therefore I’ll surely not waste your time discussing the (dis)advantages of Transpiling-to-JS vs. Writing-in-JS. Instead, I’ll try to describe the advantages of PureScript by explaining a small demonstration app which is part of the purescript-ractive package.

I use RactiveJS not only because it’s fun to develop web apps in my spare time but also in my daily work at advarics GmbH. All of our internal and consumer web apps are built with RactiveJS and therefore I’ll allow myself to say that everything I say about RactiveJS is based on some real-world experience. One thing is to play around with some nice toy but something totally different when you do your daily business with it and the customer satisfaction depends on it.

PureScript is something we’re currently testing internally and this is one of the reasons why I adopted the aforementioned GitHub project. Our company benefits from Open-Source and we’d like to give something back. At least via some libraries, code-examples, and articles. Being productive with JavaScript is something one can achieve rather easily because there’s no shortage of powerful tool-chains. But everything comes at a certain price: extreme spaghetti code, countless modules, unclear inter-dependencies, unclear state-management and side-effects, no type-system while coding etc.

Being productive with JavaScript isn’t a problem. Staying productive after the first few months of enthusiasm is the harder part.

There are so many articles describing the deficiencies of JavaScript and what can and should be done to make it a better fit to enterprise-grade projects. We have ES6, TypeScript, different tool-chains which all try to help us avoid blind spots in this very flexible, and yes: powerful language. However, I think that none of them truly removes the deficiencies but only provides more or less strong make-ups. ES6 brings a lot of fresh air but it can’t remove the older warts without breaking the whole web. TypeScript imitates a strong type system but in fact we should just call it a “warning system”. There’s not much “Type” in TypeScript just like there’s not much “class” or “let” in ES6. You can’t break the web no matter how honest your intentions are. JavaScript is here to stay for a long, long time and what you should do instead is to accept the hard truth: it’s way easier to learn a new language that force the older one to behave differently.

And this is the point where PureScript comes in.

Haskell, mighty Haskell.

I’m by no means an experienced PureScript developer. I’m just a beginner and trying to learn it from two different perspectives: as a functional-languages enthusiast and as an employee trying to find the best solutions for real-world problems. And one of the biggest problems most developers facing today is the management of state and side-effects. In our interconnected world that knows no rest and offers no pause-button the search for an ideal state-management solution transcends into the Quest for a Holy Grail. Knowing your own state and the inevitable side-effects is already complex enough but controlling all states and their side-effects around you is actually impossible. At least as long as you’re running after them and trying to control them all at the same time. Haskell, this legendary, partly loved partly derided language offers a completely different approach: open hostility to all kinds of side-effects and uncontrolled state. Haskell is the bouncer among the programming languages. And PureScript is based on its DNA. And in combination with Ractive’s powerful Parallel DOM, which handles the DOM-transformations in a functional way, the two truly become a Dynamic Duo for real-world Web Development. On one side there’s a purely functional language with real types and no uncontrolled state, and on the other a low-footprint UI-library for manipulating the HTML DOM without manually trashing it. Of course, this is only one of many possible solutions and PureScript offers bindings for other libraries like React or virtual-dom (Halogen). You may completely disagree with me. Now let me describe the demo app that I’ll be using to describe some important parts of PureScript and RactiveJS.

Building a WebApp with PureScript

There’s a single PureScript file called app.purs under demo/scripts. PureScript files have the extension .purs by default. The compiler generates a very readable JavaScript code which can be used to analyze the inner workings of the app. This is very helpful for checking the innards of foreign libraries, for example. To build the demo I use Gulp in combination with WebPack. The configuration is located in Gulpfile.js respective WebPack.conf.js. I’m not discussing the details of Gulp/WebPack here because that’s a completely different story. Use your preferred build management system (this could also be just a package.json with npm).

demo_app_tree

A PureScript source file describes a module comprising of several parts (not all of them are mandatory): module definition, imports, type aliases, instance commands (not to be confused with OO-instances) etc. At the first sight the syntax may feel very alien and just don’t expect yourself to “grok” PureScript after a few trials. A really productive strategy would be to read the fantastic book written by PureScript’s creator, Phil Freeman.

purescript_book

You can get it for free but I strongly recommend you to buy it. The money goes to a charity that organizes code-learning clubs for kids. So, this is a reason enough to spend a few $ or €, isn’t it?

And this is how a PureScript code file may look like. In fact, this is the app.purs file from the demo app. I deliberately removed the comments. They’re available in the GitHub version, of course.

app_purs

Modules, so many modules

It may feel awkward at the beginning but the structure of a typical PureScript source file is actually very clean and much better organized than most of the JS-scripts: Here we see the beginning of the module indicated by the keyword module, followed by its name. The name itself can be separated with dots which indicate namespaces. Only the last name counts as a module name. After the module name you often write several imports which include some standard modules like Prelude or Control.Monad.Eff (that helps you working with the dreaded state and “effects” caused by Console, DOM-manipulation, random-number generation and so on). Often you’ll find yourself importing many different modules and therefore you should consider using packages like purescript-batteries that do this job for you. I did not use it in this demo app because, in my opinion, a demo should not use the latest and greatest of the available tools. A demo has to be simple and pure. However, when developing complex apps you should definitely use something like purescript-batteries. In general, when searching for proven solutions of standard problems consider visiting Pursuit, the documentations & package database for PureScript.

A module in PureScript is basically a source file optionally exporting some functionalities and/or types. For example the Prelude module in the example above exports many different things but we’re interested only in Unit, bind and not. This is why we put them in parentheses after the module name. We indicate that we’re only interested in these three exports. Everything else remains invisible to us. If we’d like to import everything at once we could just use (..). Also we could simply ignore it completely but the compiler would complain and shows us a warning like this:

ps_compiler_warning_import

We see that the compiler correctly inferred only three imports and rightfully warns us. This is one of the best features of PureScript that not only helps when dealing with imports but also with function signatures. This is, for example, what happens when I remove the signature from the setRandom function (line 15 in the picture above).

ps_compiler_warning_function_signature

The compiler infers the correct signature for us so we can simply copy/paste it.

Functions are everywhere

I’d rather avoid theoretical discussions about functional programming and which language is more or less functional. Let’s be pragmatists: functional programming is about managing state and side-effects (some even distinguish between side-causes and side-effects). PureScript is a functional language because it’s managing state and side-effects so perfectly that there are no side-effects allowed at all (more precisely: no uncontrolled side-effects). The usability of such programs is questionable because we need some kind of state and state-change is inevitable. So, how does PureScript “allow” state and side-effects without losing its functional street credibility? Enter Monads. 🙂

No, I don’t think that it’s the right time to describe monads in fully. Actually, I tried it once by using Scala and now I think I failed completely. OK, I provided some Scala code and a few nice pictures,…but I couldn’t properly describe Monads! However, this isn’t because they’re “hard to understand” but because they’re “hard to explain”. For the purposes of this articles, just imagine Monads were some kind of Callback-Functions with some extra powers. Monads are Callbacks. And because Haskell hates side-effects and PureScript is Haskell reinvented, it’s obvious that it shares the same hate towards side-effects (and any kind of “free riding” state). Well, what should be done when you try to control some state? Just use a Callback when you’re done and PureScript will continue its work, just as if there were neither a state nor any side-effects. Um, did I say “Callback”? Actually, I wanted to say “Monad”. Yes, just use a Monad if you’d like to keep your friendship with the PureScript compiler intact. PureScript will never, ever, deal with state-changes and its side-effects. For such dirty stuff you have to use Monads and also to properly declare them. In the function signature, of course. One by one. No Monad left unregistered! try to “forget” a Monad and PureScript will catch you immediately (remember the automatic function signature inference from above?).  You can also use an alternative way of setting a special expression that automatically expands to any kind of side-effect that deserves a Monad. The real power of PureScript lies in its extreme expressivity. Let’s look at the setRandom signature and try to spot a monad:

monad_example

First, we see the name setRandom is repeated twice. The first row contains the function signature (easily distinguished from the implementation below by the double colon ::). We see some unknown elements that obviously have nothing to do with Monads or any other, more esoteric stuff from the world of Haskell/PureScript. Like any other function this one has a name and some parameters with type information, as it seems. PureScript, being a strongly typed language, expects you to provide precise information about all the types you’re going to use. It is not possible to use anything inside a function that wasn’t declared in its signature. This means that you can check the inner working of a function just by looking at its signature. Try this with JavaScript, Java, C++, C# or any other non-purely-functional language and I’m sure you’ll instinctively understand why state management is so hard with these languages. Now, at the beginning of the signature above we see forall e. This can be read the same way we read mathematical formulas.

For all elements of function X blah, blah, blah“. It’s really like simple high-school math. Now, let’s try to read the signature:

setRandom is defined as a function for all elements of type e. -> Ractive -> Eff (random :: RANDOM, ractiveM :: RactiveM | e) Unit. … Ok, we now understand something from the beginning of the signature but not the rest of it. What does this “arrow” mean? Why does PureScript use parentheses in such a weird way?

First, the “arrow” indicates a very important aspect of PureScript that deals with a (presumably) less known fact that PureScript only has functions with one parameter. There are no multi-parameter functions in PureScript! Even if you say, for example: I pass to function X three arguments of type Number, String, Number, PureScript would still call three functions, each with one of the given arguments. We also say: PureScript functions are curried. Just imagine, PureScript would always conflate the functions to just one parameter. And the aforementioned “arrow” symbol indicates (and separates) function parameters from each other. Additionally, and more importantly, it shows us the input and return values of each of the functions. This means, for example, that setRandom‘s first application with the input value of type Ractive returns an Eff-Monad which then becomes a new input of the second application. It’s basically a chain reaction continuously forwarding values from the last application to the next one.

So, let’s try to read the function signature again:

setRandom is defined as a function for all elements of type e. that’s being applied to its argument of type Ractive, which returns a resulting function that’s being applied to its argument of type Eff

We have used the verb “read” two times because we have to consume two arguments by applying two functions. The first one is Ractive (well, it has something to do with the RactiveJS, that’s for sure) and the second one is Eff (Did you spot a Monad?).

We also say “to apply” and not “to call” a function. In PureScript we do not call them, we apply them. Why? Because a function in PureScript is based on math and a function in mathematical sense is an association between the elements of two sets. Here’s a simple example of the first three Latin alphabet letters mapping to their ordinal numbers.

function_application

Let us now reiterate the complete application of the setRandom function:

We have two applications, one with the Ractive-argument, another with Eff-argument, but what’s the meaning of parentheses after Eff? Another function application? No, absolutely not. Brackets in PureScript have a completely different meaning and because all functions contain only one argument there’s simply no reason to use parentheses as mandatory parts of any function signature. In this example the parentheses describe the structure of an argument of type Eff. Let’s concentrate on Eff only, just to learn a few facts which apply to PureScript structures in general.

Eff (random :: RANDOM, ractiveM :: RactiveM | e)

First, Eff is the name of the abstraction for dealing with side-effects. It’s an integral part of PureScript’s ecosystem. Just look again at the import list and you’ll spot several Eff‘s. For Haskell users among you, the Eff is PureScript’s IO-Monad. For readers without functional background: Eff is the abstraction in PureScript for describing side-effects. A side-effect is anything that changes a state, like a random number generation, DOM manipulation, writing to the console, to printers, to networks etc. Anything that manipulates some kind of state. And because there are many different changes that can happen, PureScript expect us to describe what kinds of “effects” could happen during a certain function application. This is the reason why we use Eff and write all these names inside parentheses. The parentheses describe the different “effects” that can or will happen during the function application. Ultimately, by naming the different effects inside the parentheses we define the structure of our monadic function argument that is based on the type Eff. We see RANDOM and RactiveM, and also we see some weird combination of pipe character | and e. These three elements are fields of the structure under Eff. A simple analogy to OO-programming would be “class properties”.

Well, the name RANDOM is familiar to us, it must be some kind of random number generation, After all, our function is named setRandom, so this shouldn’t be a big secret to us. But what about RactiveM and this pipe-and-e combination. RactiveM is just another Monad describing a separate effect defined in the purescript-ractive library. This means that we can also define our own Eff-Monads and are not confined to standard ones (like console, random, DOM etc.). If we want to indicate some special state changes, like manipulating RactiveJS-instances for example, we just define our own monads. This is how it looks like for RactiveM from Ractive.purs (in src/Control/Monad/Eff):

ractive_monad

A rather simple definition for something that’s capable of doing such complex things, isn’t it? We just used the very flexible and powerful FFI (foreign function interface) of PureScript. The keywords foreign import indicate that we want to take something from the non-PureScript world (JavaScript) and import it as an ADT (algebraic data type). After the double colon we define its kind to be an Effect. In PureScript we indicate them by using the exclamation marks. Described in more simple terms we could say that we want to create a mapping to some non-functional, side-effects producing “thing” from JavaScript that should be known as RactiveM in our purely functional execution environment. There’s a lot to be said about FFI, so please visit the link given at the beginning of this paragraph. Also, there’s an important distinction between types and kinds in PureScript. A type is a set of possible values for some expression. For example, the type Boolean is a set of two possible values: (true, false). This means that each and every expression returning a Boolean value can only be either true or false. If we have an expression delivering a Number then the amount of possible return values will be much larger. But nevertheless, it’s still a set of discrete values. In short, types are sets. And what about types themselves? Are there any sets describing types? How do we distinguish types from each other? Well, it’s simple: we have kinds of types. In the above example our RactiveM structure was rightfully declared as being ‘some’ kind of effect. We did not describe its structure in detail but rather its kind and this was good enough for the PureScript compiler to accept it. This is the way we can, for example, easily integrate external, non-PureScript structure into our code.

Interleaving Effects

And what about the lower-case e? The e is actually a template that signals the possibility of embedding further effects not explicitly defined inside the parentheses. This means that we can apply setRandom to some additional effectful changes and the function would integrate them automatically as elements defined by the lower-case e. The e acts as a placeholder for all not directly named effectful changes. It helps us to specialize our function for new effect types. From the opposite perspective this also means that we can be more rigid by simply removing the e from the “parameter list” and block any possibility of accepting any additional effectful changes we didn’t declare in advance. Now, let’s try to read the function signature again:

setRandom is defined as a function for all possible effects of type e which is applied first to the argument of type Ractive, then to the argument Eff whose structure comprises of effects of type RANDOM and RactiveM and can be specialized to additional effects of type e.

In the PureScript Book you’ll find a much better explanation of the forall-keyword and the parameter e. It’s called a quantified or polymorphic type which isn’t specialized in advance. This doesn’t mean that PureScript supports duck-typing like Ruby or Python because the types are always known at runtime. Some also call it “static duck typing” because we can substitute different types for symbols like e.

There’s one more thing I deliberately left out: the Unit after the closing bracket of Eff. This is the return type of the Eff-Monad. Usually, a function’s return value is indicated by the type after the last arrow in a PureScript function (look at the definition of the function inverse, for example, that returns a Boolean). But in this case there’s no arrow behind the monad Eff. This is not some weird exception but a standard behavior. Remember, Monads are Callbacks, so you don’t get any return value directly from a callback but only after it’s completed its tasks. Therefore, the return value of setRandom is a structure of type Unit which can be interpreted as “void” in C#, Java etc. Practically, there’s no return value at all. So, a complete description from above should end with: and returns a value of type Unit.

Using RactiveJS

Among many other things RactiveJS has components, a flexible event handling and adaptors. And as already mentioned at the beginning of this article, much of our internal and customer-facing infrastructure is based on this powerful library. Just like many other, more widely known libraries, RactiveJS contains a specialized structure that describes the state of the browser’s DOM (document object model). In Ractive’s lingo this is called Parallel DOM and is basically a simplified clone of the original DOM with all the events, bindings and other states from the original document. Dealing with Parallel DOM instead of direct DOM manipulation is very beneficial because it helps to avoid the dreaded DOM trashing and spaghetti code with unmanageable inter-dependencies. Parallel DOM lets us handle the states of the DOM just like return values of functions. At any point in time we’re only interested in the final state of the DOM and not how it gets calculated. With RactiveJS we declare the state of the DOM and leave all the hard work for the internal mechanics of this UI library. I suppose that many of you have already heard of React and is much praised Virtual DOM. Well, Ractive’s Parallel DOM is partially similar (but not structurally, because there’s only one Parallel DOM while React maintains many little Virtual DOMs, for each of its components). Also, Parallel DOM is much faster than React’s Virtual DOM. Until now we have used RactiveJS only in combination with JavaScript and it served us well. But the progress is inevitable and the more power you integrate into your infrastructure the more responsibility you create. To handle the future complexities lurking in our Web Apps we’re currently testing PureScript as an additional, type-safe, side-effect-free layer between the raw JavaScript and the already very functional and declarative RactiveJS library. In many ways we consider RactiveJS as a perfect fit for PureScript, and vice versa, because:

  • it’s only a library and not a ‘framework’
  • it’s declarative by default
  • it doesn’t expect you to manipulate the DOM by hand
  • it offers syntactic sugars like proxy events
  • it adapts very nicely to other libraries via adaptors
  • and last but not least, it get’s completely out of your way.

To create a proper mapping between JavaScript and PureScript a proper binding between the two is needed. And as already mentioned, PureScript offers an easy-to-use option called FFI. Here we define a small library inside src/Control/Monad/Eff/Ractive.purs that maps Ractive’s APIs to their purely functional counterparts in PureScript. In this file and it’s JavaScript-companion called Ractive.js we define the PureScript mappings and JavaScript implementations. For example, we have the API call on which defines new proxy events and their handlers. There’s also a corresponding API off which un-registers such events and handlers.

on_off_apis

We see some already known elements like foreign import, forall etc. We also see that the second parameter contains only parentheses but nothing in front of them. So, this isn’t a monad like Eff in the previous example. Also, there are two additional arrows indicating and the signature clearly states that the function returns a value directly. But this time it comprises of two parts: RactiveEff and Cancellable. Are these two types returned at the same time? Maybe subsequently? No, it’s not that simple. This return value is a type alias defined at the beginning of the Ractive.purs file:

ractive_eff_type_alias

RactiveEff is an Eff-Monad capable of interleaving the effects of type e together with the predefined RactiveM effect and it returns a value of the given type a. Ultimately, the RactiveEff type alias is a type constructor that creates a Monad of type Eff which returns a value of type Cancellable. The Cancellable value is based on the result returned from RactiveJS which is capable of cancelling the registered event handlers. The kind of Cancellable type is defined here:

cancellable_kind

The star symbol signals the kind “value”. We now know that there are two kinds named “effect” and “value”. But there’s more in PureScript world. We also have kinds like * -> * that describe functions that map from types to types (hence the “arrow” between the stars). A typical example is the List type constructor. To test its behavior we should use PureScript’s REPL (read-evaluate-print-loop) called PSCi.

psci_repl

I’m using here the standard tool for building and managing PureScript packages called  pulp to invoke the REPL and you should consider using it too. The option “-m” stands for multi-line so I can insert several lines before letting the code being executed via CTRL+D. Then I import the module defining the type Data.List.List and display its kind via command :k (or in longer form :kind). Here we see that PureScript’s List is a type constructor, a function from types (*) to (->) types (*). In fact we can’t create lists of elements like in other languages. There’s no way to do something like Mylist = new List(1,2,3,4,5). Instead we first have to create a new type constructor, for example List a, capable of constructing Lists that contain elements of type and then apply it to each of the future elements of this list. Remember, in PureScript we never call a function but only apply it. So, the same rule “applies” when constructing compound structures like Lists. Simpy spoken: PureScript has an additional layer of indirection between the “ordinary” constructors, similar to those in C# or Java, that create instances of types and type constructors that create those constructors, like List a.

Mapping from PureScript to JavaScript

We have seen the purely functional side of JavaScript in Ractive.purs. We’ve defined types, type constructors and direct mappings to RactiveJS APIs like on and off. Now we have to learn a few bits about the impure JavaScript side which is built upon the well-known CommonJS module format popularized by NodeJS. All we have to do is to make the needed RactiveJS APIs publicly available via NodeJS’ exports in the companion source file RactiveJS.js. Here’s a part of this file describing some of the available API-calls.

ractive_js_companion_file

At the first sight the calls look very convoluted but the reason for this is quite simple: PureScript doesn’t have multi-parameter functions so each of the parameters has to be used separately in a single function call. We see two main API definitions: on and off. At the bottom of the file is the usual module.exports definition. This is all we need to make PureScript’s FFI happy. But beware the fact that often you’ll have to adjust your code by providing extra function calls or returning wrapper objects. For example, when PureScript expects a monad as a return value you’ll mostly have to wrap such values with a thin wrapper on JavaScript-side. Here’s an excellent article on building a Twitter Bot from Joel Grus where he describes some intricacies and problems with mapping of JavaScript-APIs to PureScript.

Now, let us take the API call on as the example on how to access JavaScript functions from PureScript. We see in the above JS-code that on expects three parameters: event, handler and ractive. It uses them to register a proxy-event, its corresponding handler and a ractive instance to wire them together. On PureScript’s side the function application looks almost similar but there’s a significant difference between the two: the second argument in PureScript’s on is wrapped in parentheses comprising of three types Ractive, Event and Eff e a. This structure describes a callback function that acts as the event-handler of the registered proxy-event which is called by RactiveJS itself! At the beginning of app.purs we see two of them:

event_handlers

onLogoClicked and onControlButtonClicked are structurally almost the same. The first one expects just one more effect, RANDOM, but the rest of the signature is interchangeable. We see that both function applications manipulate our Ractive instance and therefore make the usage of the RactiveM-Monad mandatory. Because we now know how to read function signatures their internal behavior is not a big secret anymore. On each click on the PureScript-Logo in the demo app a new radom number will be generated by applying the function we already know very well: setRandom. There are some additional constructs we surely know from other languages: if…then…else. Our little app can decide if the random number should be generated or not. If not then a message is made visible in the text field (“message”) of the Ractive instance. To set this message in the Ractive instance (that is: to change its internal state) another function is needed: change

change_function

Here we see the application of yet another RactiveJS API, the set-function. What we’re doing here is just a simple wrapping of the raw API-calls. The rest of the code of this demo follows the same strategy: there’s a Ractive instance to be manipulated (side-effects!), so we need a proper Monad to separate the impure world of JavaScript from the pure one of PureScript. We create a function call in the JavaScript companion file and map it to a function application via PureScript’s FFI.

Here’s a small video showing the demo in action.

Conclusion

PureScript is wonderful and wonderfully simple. The same applies to RactiveJS. It’s small and simple and doesn’t try to teach the “right way” to code your web apps. Both of them are not complex. What’s complex are our own habits. Habits that lead to spaghetti code, hidden state-changes, mutable-by-default values, convoluted dependencies etc. It’s also our own laziness when it comes to find the best solutions. Often the “best” solutions are just cheapest or quickest ones that sooner or later reveal their not-so-cheap nature. We, developers, deserve better environments. And our customers deserve better products that can improve and extend over time without incurring technological debt or sudden interruptions and crashes. This article is by no means a proper introduction to PureScript and/or RactiveJS. It’s just a small example on what can be achieved when we combine declarative UI-libraries with purely functional languages.

Leave a Reply to brakmic Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 thoughts on “WebApps with PureScript & RactiveJS”