Software is hard
Software is hard

Managing Application State with PureScript & Redux

19 minutes read

PureScript-Redux is a small library which helps to utilize the Redux state container with PureScript. Although I have almost no experience with React, which is the most prominent ecosystem for using Redux, I thought it would be a nice learning exercise to create a set of Redux-Bindings for writing WebApps in PureScript. Redux itself is heavily, and rightfully so, promoting the benefits of using pure functions for managing the application state and PureScript, being a Haskell-dialect, is a purely functional language. Therefore, it seemed to me very logical to try to combine them together. But because I’m also a PureScript-Beginner and still learning the language I can’t give you any warranty that this implementation follows all the proper idioms and/or best practices.

Before we start I’d like to ask others for help to make my current implementation less buggy, more versatile, and more idiomatic.

Now, let me quickly write down a few words on Redux and how I understand its mechanisms. If you find any mistakes, please, leave a comment. Thanks!

Redux – predicticting application state without drowning in Flux

After React went mainstream soon it became clear that even the fastest possible View-Engine alone won’t help you much in creating complete Web Applications. I still remember asking myself how I should combine React with my Backbone models, routers and other machinery. I was interested in React but it made a little sense to me to play with the View-Layer alone hoping the rest will ‘somehow’ fit together. Luckily, the Flux architecture came out….and created even more confusion in my (only my?) brain. It was hailed as a ‘clear‘ and ‘easy to understand‘ unidirectional architecture that will help us avoid the typical problems of MVC where you end up with Event-Spaghetti spilled all over your code and templates. Well, I still have problems to understand Flux as a whole but this isn’t that important to me today. After having given up Flux I also stopped learning React because I was already very happy with RactiveJS and AmpersandJS (a more modular version of BackboneJS). RactiveJS gives me all the power of real declarative programming combined with its super-fast Parallel DOM (roughly like React’s Virtual DOM, only faster), and numerous AmpersandJS modules offer me everything I need for my daily work and private projects.

In the mean time (2014-15) many Flux replacements appeared on the stage but if I’m not mistaken, most of them are trying to make Flux ‘easier to digest‘ without changing anything substantial at the core of its architecture. However, I’m not trying to incite any flame wars because this is a technical blog and I’ll always provide some code in my articles. Therefore I’ll stop now just as I’ve stopped learning React two years ago when it became obvious to me that the complex React/Flux-architecture was yet another path to nowhere for ordinary developers like me.

But one particular library got my attention because it didn’t try to make Flux be less Flux but instead avoided it completely: Redux. Without any dependencies, very lightweight (2kb only) and enforcing real functional purity in JavaScript it was a welcoming breath of fresh air in the dusty fluxian machine room. Redux knows only one state and has no dispatchers at all. Whenever you want to change the state of your application you create an action and send it. This action is then received by a reducer that changes the state and returns it back to you. The reducer is a pure function and the sole entrance to the application state. There’s no possibility to change the state by avoiding the reducer. You always have to use an action creator (also a function) to create a new action that is basically a JSON structure with properties describing what happened to the state and optionally some additional data that should be involved in the state-change. Actually, there’s no ‘state change’ as we might expect it. You always get a new state, not the modified copy of the previous one. For ‘functional developers’ this is nothing new as they already know that there are no mutable variables and immutability is given (and enforced!) by default. But as we all know, despite the fact that JavaScript treats functions as first-class citizens, it still allows side-effects and mutability. Now, Redux comes in not only to build Flux architectures without Flux but also to make JavaScript ‘purely’ functional. Well, it would be dishonest to say that JavaScript can be made purely functional because functional purity is not only a semantic concept. The language syntax itself has to support it and this isn’t given by default when you use JavaScript. Just use the default Reducer example from Redux’ webpage an you’ll immediately recognize that despite all claims one’s still able to introduce of side-effects without being stopped by Redux and/or JavaScript:

reducer_example_from_redux

Of course, the function always returns a new state but I could still do any kind of ‘effectful things’ and JavaScript could never stop me from doing it. I know, it would be crazy to do something like this but there’s a slight difference between “I’m crazy and can go crazy at will and “I’m crazy but can’t do anything harmful. And it was exactly this demo-function that gave me the idea to try to build a purely functional wrapper so I could build a Web Application by only using PureScript. Additionally, the support for Middleware written in PureScript is also available and we’ll soon see a rewrite of the original Logger-Example from Redux’ Homepage.

PureScript-Redux + RactiveJS

It’s not a secret that I’m a happy user of RactiveJS because it gives me everything I need from a UI-Library without forcing me to obey to the rules of [put-any-IT-ideology-here]. To make the demo more complete and a little bit more realistic we’ll combine Redux, PureScript and RactiveJS  to create a Web App with a Bootstrap-based UI, some event handling, a Redux-listener and Logging-Middleware.

Everything will be written in PureScript.

demo_app

The Redux API

Currently, the following Redux APIs are supported:

redux_api

Like with many other libraries that map between PureScript and JavaScript we have to write several foreign imports that map to the ‘code behind’ which lives in a JS source file of the same name. For example, this is how the implementation of createStore foreign import looks like.

createStore_foreign_import

At the first glance this source looks like any other CommonJS module. It has an require, a few function references and, more importantly, a special comment at the beginning of the file. Every PureScript-aware module that contains foreign import definitions must have such a comment with the fully qualified module name. In our case the library is named Control.Monad.Eff.Redux. The actual file doesn’t have to have such a long name. Redux.js is sufficient enough. Its PureScript counterpart, of course, is named Redux.purs. Also, we notice the several nested function calls. This is because PureScript’s functions have only one parameter and to make both sides (JS/PS) happy we have to cascade the arguments over multiple function calls. There’s also an alternative way of doing it via certain helper libraries from PureScript that can take care of all these arguments but I’m not using them here for now. As we can see the _createStore function doesn’t do much. It basically receives both parameters (reducer, initialState) and calls the real createStore function that we’ve referenced already at the beginning of the module. Finally, we return the new store. Now, let’s quickly switch over to PureScript to learn about applying createStore from there.

As we already know the createStore-API expects us to deliver a proper Reducer-function. In our case this will be a simple counter function that takes a valid Action ‘object’ and according to the command inside action increments or decrements the current state. If we receive an invalid Action we’ll simply return the current state.

reducer_function_in_purescript

To register our Reducer we use the createStore foreign import and give it the initial state of Int 1.

createStore_call

Subsequently we initialize our RactiveJS application by giving it a bunch of configuration properties. I’m not discussing the creation of RactiveJS apps here because there are already a few articles on this subject available on this blog. I’m also maintaining a library with RactiveJS bindings for PureScript and I’d like to hear your opinions about it (email, comments, tweets, telepathy etc.).

What’s important in this app is that we also give it the store reference we got from the previous createStore-call. We now have our state at the center of our app and we know that there’s only one possibility to change it. With this strategy we centralize our application state. There may be other strategies too and presumably much better than this one. What’s important is that we not only logically (Redux) centralize our application’s state but physically too, by putting it in a self-contained component. And RactiveJS instances are components.

initialize_app

Finally, we subscribe to a few events. I’ll also avoid talking much about proxy-events under Ractive and how to declare them. There’s an article on that in the case someone is interested in this topic.

subscribe_to_events

Now, after we’ve successfully built the app with the help of Gulp and WebPack we open the index.html and click the two buttons to see some output in the console:

app_console_output

Well, this looks ‘OK’ but somewhat different than the console output at the beginning of the article. If you go back and double-check you’ll see that here’s no middleware at work! Those of you who know Redux much better than I do would immediately recognize the difference: I wasn’t using any kind of Store Enhancer to introduce some Middleware in our Redux instance! The default enhancer for Redux, and the only one shipping with it, is the function applyMiddleware which is also available as a foreign import. Let’s see how this function works.

Enhancing the Store with Middlewares

We can extend the default functionality of Redux by wrapping it’s store’s dispatch method. Just for those of you who have never used Redux before: every Store contains an API comprising of these functions:

The dispatch function is responsible for state changes as it dispatches actions that ultimately lead to state changes. Now, if we somehow hook into this process-chain we’d get a pretty cool option to execute additional tasks before the actual state-change happens (or even influence it). And this is what applyMiddleware does for us. But first, let’s look at its implementation in Redux.js:

applyMiddleware_foreign_import

We’ve already accepted the fact that JavaScript needs a few interleaved function calls but the essence is quite simple: we take an array of Midlewares (yes, we can register many of them at once) and call the internal reference of the original createStore by providing it applyMiddleware as the third argument (please, ignore the suffixes ‘Internal‘ as we only use them to distinguish between original functions and their internal references in the JS-file).

Note that this way of registering Middleware is only possible since v3.1.0 of Redux. Previously, Redux used a more complex logic where you’d have to create a ‘pimped’ version of createStore called createStoreWithMiddleware by using applyMiddleware in a separate call. The returned value from applyMiddleware would then be an expanded version of createStore that you’d have to feed it with the initial reducer and initial state.

Now we can use applyMiddleware with PureScript to replace the createStore application in our demo app. Additionally, we define a Logging-Middleware by creating a function named simpleLogger. According to the rules of the Middleware API from Redux this function expects certain arguments and applies a function called next as its last expression. Notice: I’m sometimes using JavaScript-lingo here as there are no ‘function calls’ and ‘multiple function arguments’ in PureScript. The aforementioned next-Function is actually the next dispatch call in the execution chain as described in Redux-documentation. At the first sight the application of the whole Middleware logic seems rather convoluted and complex but its execution flow is rather simple:

  • take any number of hooked-in functions which want to do something before the default dispatch method reaches the Reducer (and the state gets changed, of course)
  • take care that all of them are being executed in order (that is, no multiple calls and no function left behind)
  • to make them play in concert they’ll be composed from right to left (effectively, this means that every function will have a reference to the dispatch function of the next Middleware in chain)
  • ultimately, we’ll create a single dispatch function based on all those Middleware-functions. This final function will call all of the specialized dispatches one after another until it reaches the original dispatcher-function and grabs the store itself.

A more detailed explanation of the inner workings of Middleware API can be found here.

applyMiddleware_in_action

At the bottom of the above image we see the definition of an middleware array and application of applyMiddleware. We also declare the counter function as our Reducer with initial state of Int 1. Above the main function is the Logging mechanism defined. Following the layout of the Middleware API it expects these references as arguments:

  • Store [note: this is not a complete Store object as is contains only a subset of the original Store API]
  • Next (the reference to the next dispatcher function in chain)
  • Action (which will serve as the argument of the next-dispatch call)

Therefore, the result of the Logger-function application is always a next(action) application which leads to the next Middleware in chain (if there are more of them) and so on. This harmlessly looking next(action) provokes a chain reaction that at its end applies the original dispatch-function to ultimately change the application state.

Playing with the Demo App

Our application exposes two buttons that generate certain events which create Actions. I should also note that I’m not using any Action Creators here because the demo application is really small and comprising only of two Actions. There was no need to add a superfluous Action Creator that would only produce two simple JSON structures. However, when developing real applications you should consider using Action Creators or similar mechanisms or you’ll end up chasing configuration objects, their properties, unkempt constant objects etc.

Just to make it more visible where the events come from here’s a part of the application template built with Ractive’s Mustaches. Here’s more info on Proxy Events and how to build Components with RactiveJS.

ractive_proxy_events

We declare two events which will be bound to certain DOM-elements, our two buttons. There’s no logic, not JavaScript calls, nothing else but plain declarations. Our event-logic is located in the app.purs where the rest of our machinery lives. Of course, it doesn’t have to be that way and usually it isn’t. I doubt you’d like to put everything in a single source file.

Now, when we, for example, click on the Decrement-Button the following piece of logic will be executed delivering a new Action object.

onDecrementClicked

We get our Store object from our App by using Ractive’s get-function. Please note that we’re not doing any kind of ‘assignments’ here. Actually weren’t doing it anywhere else in the code because PureScript doesn’t know anything about value assignments. The meaning of these arrows is quite different and has much to do with Monads but I’ll avoid any deep explanation of Monads because I’m not the right person to handle such complexities. Yes, I tried it once and I still think it was a disaster but I’m trying to remain honest and will not remove the article, so you can laugh at me.

However, after we’ve received our store reference we use the next foreign import function named dispatch that takes an Action that we’ll directly declare (remember, you’ll usually want to utilize some Action Creators instead). Additionally, we forward it the Store reference too. Now, we want to see the innards of our dispatch JavaScript code.

diaspatch_foreign_import

Again, we recognize the same pattern here. Interleaved function calls each consuming a single arguments. This awkwardly looking action.value0 is because of the discrepancy between JavaScript’s and PureScript’s handling of Types. PureScript allows us to use sophisticated Algebraic Data Types which are Meta-Types that define ‘ordinary’ Types and JavaScript simply has no such concept. Therefore, we have to lay down an option to deal with values based on such Type Constructors. But this isn’t the whole story as there’s also a possibility of having optional values, like those based on the ubiquitous Maybe kind. In such cases we’d have to go deeper and first check if the constructor property’s name of such an action equals to ‘Just‘ and if this is the case the value from value0 can be used. In the opposite case (‘Nothing‘) we’d have to apply some logic that prevents further execution because it’s obvious that there was nothing instead of ‘Just SomeValue‘. But, I’m still learning new techniques to handle such cases, and if someone of you has a cool idea on how to handle this better, please, let me know so we can make this library more useful.

After we’ve clicked on the button and generated the event, our dispatch will fire the Action object (internally, as shown in the picture above, the Store object will do this for us). This will ultimately lead to an internal calling of getState and returning of a new state value which will be considered as the current state. At the same time all previously registered listeners will be notified about this state change. In our case the listener is a simple logging function (not to be confused with the Logging-Middleware we saw previously!)

simple_listener

As we see here there’s not much logic in it. We only ask for the current State and show it on the console. It’s really important to differentiate between listeners that react to changes and middlewares which drive the change in Redux. A listener can only listen to changes but not hook into the actual state-change process. To make Redux aware of such a listener we have to use another method from its API called subscribe which, of course, is also available as a foreign import:

listener_subscribing_in_redux

Don’t be confused by this seemingly superfluous store reference. Both are needed because the inner store is being used to curry the listener-function which is a lambda (or callback in JavaScript-lingo). In the picture showing the definition of numericListener you can recognize the backslash which always indicates a lambda in PureScript (and Haskell too). We simply bind the current store to the callback because as long as the App lives the Store will remain the same. It would make a little sense to arbitrarily change the Store or even pass it again and again.

Conclusion

Redux is powerful and despite its small size not always easy to grasp. There’s a lot going on inside its sophisticated machinery and it took me quire some time to understand all its Middleware stuff. Maybe I belong to the minority that’s had hard time with understanding of Redux’ Middleware but the development of purescript-redux helped me a lot to get a better insight into Redux’ innards. I’m still not quite sure if my understanding is complete enough to justify the development of purescript-redux but because there wasn’t any other PureScript implementation I could only try it by myself. If there’s someone else interested in development of it, please, drop me an email, a comment, a tweet, or send a pull request. Thank you!

Leave a comment

Your email address will not be published.

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