At Instacart, we have increasingly been using a reactive functional approach to manage state. The goal is to have a deterministic Android application that is easy to understand, test and maintain.

Let’s say we have a form to add a new comment. Basic requirements are:

1. There is a single text field that the user can modify.

2. There is a submit button that the user can press to add a comment.

3. We only enable the submit button when comment entered is longer than 5 characters.

4. Disable the submit button while we are uploading the comment

How would we approach this problem in a reactive functional way? First, let’s define a data class that defines our view state:

Now, let’s define how our view will look:

We laid out the basic contract for the view. In this case, we made the view as stupid as possible. It doesn’t contain any logic besides taking an immutable state object and updating itself. When user types a new letter, we trigger onTextEntered callback. We then create a new CommentFormViewState object and pass it back to setViewState for the view to re-render.

CommentFormViewState is made up of data and callbacks. It is the final product that the view sees. The question is how we update the state after the callback is triggered? To do this, let’s first create a new class that represents only the data and a couple of classes to represent the user events.

Note: if you don’t know what Lce is, you can find info about it here (basically it’s request state)

There are two actions that the user can take:

Now, that we have defined data and user actions, we can model the relationship between them. Here is a basic signature of the function that takes user event streams and returns user data stream.

Before we implement this function, we need to introduce a concept, common in functional state management, called reducer. A reducer is a function that takes an event, the current state and returns a new state object.

Here is a signature of a reducer function:

fun reduce(event: E, state: T): T

Reducers are supposed to be pure in a functional sense, which means that two identical inputs will provide identical output.

Here is our reducer for text changed event:

Now, let’s plug this into RxJava

We still need to incorporate submit comment actions. When user clicks submit, we want to trigger a network request and keep the data updated. To be able to do that, let’s define an interface for the api.

Note: if you don’t know what Lce is, you can find info about it here (basically it’s request state)

Now that we have our service, let’s setup comment submission.

As we are working in a statically typed system, to use RxJava scan operator, all events must have a matching type or a super type. As you can see, we have an issue where the events stream data type becomes Any.

To be able to reduce, we would need check if the object is of a particular type.

if (event is TextChangedEvent) {

// Handle text changed event

} else if (event is Lce<Comment>) {

// DOES NOT COMPILE ^

}

In simple cases, it’s a bit ugly. But when using with types that have generic type parameters, it is hard to maintain type safety. Due to type erasure, you cannot safely check that the generic parameter is of a specific type.

Is there another way? Of course! Let’s take a step back and look at our reduce function

fun reduce(event: E, state: T): T

What if we apply function currying to this?

fun reduce(event: E): (T) -> T

So, what happened here? We split the function! Previously, we had a function that took two parameters and returned a state object. Now, it takes only the event and returns a function that takes the state and returns the state. Functional magic!

So, how can we use this?

We created a common type (CommentFormData) -> CommentFormData that we can now use.

To make this even better, let’s declare a kotlin typealias.

And now, let’s replace the code

We have managed to define the logic behind comment form data! Now that this part is done, let’s take the data and create a view state using it.

To finish this reactive component, let’s delegate user actions to the CommentFormModel and use the data provided to create the view state. We will use RxRelay’s to achieve this:

The final step is connecting the view model to the view.

While, we are using a presenter here to achieve integration between view model and view, it’s not the best pattern for composability. I’ve used it for simplicity sake.

You might ask, why would we model our application this way? If you are not familiar with functional patterns and RxJava, this can be intimidating. However, once you learn these concepts and start applying them, the benefits are clear and it’s hard to go back.

Testability

Each class is responsible only for one thing, code is mostly side-effect free and most of the logic lives outside of the Android framework. This enables us to easily test majority of our code using simple jUnit tests.

Predictability

The contract between classes is clearly defined. Each class has only one public method that defines it’s API. For the inputs provided, this method will give you the same deterministic output.

Maintainability

When we combine predictability and testability, we get code that is highly maintainable.

Shameless Plug

If you are interesting in building functional android applications, Instacart is hiring. Email me at laimonas.turauskas@instacart.com

For help with the article, special thanks to:

Maksim Golivkin

Colin White