Real World Functional Programming with Kotlin & Arrow

Ahmed Rizwan
AndroidPub
Published in
5 min readSep 2, 2019

--

Arrow is a functional programming tool-kit for Kotlin. We can apply a lot of the functional programming concepts with it, as we do in other purely functional programming languages.

A Real World Example

I came across this video — about functional programming in Scala by Jordan Parmer. In the video Functional programming concepts are beautifully explained and demonstrated using Scala & scalaz.

And I had the idea of achieving something similar with Kotlin & Arrow. Following the real world example in the video — the task is to map raw data to domain specific data. Pretty straight forward stuff but not too simple when considering the edge cases and code-safety.

Railway Pattern

In a flow — there can be a number of operations, any one can fail — we want to bypass to Failure as early as possible without compromising/halting the execution of the program itself.

Keeping this in mind, let’s take a look at an example, starting with the main method:

We’ll be performing three tasks:

  1. Generate raw users (dummy data)
  2. Map those raw users to Domain users
  3. Print em!

Generating RawUser data

For the purpose of this example, creating three dummy RawUsers

The model:

Model

Method for generating Dummy RawUser objects:

Raw Users Generator

Note: This data is sort of corrupt — as the second user doesn’t have a last name & in our domain model, we require one.

Mapping RawUser to DomainUser

The model:

Model

The differences between Raw and Domain data:

In RawUser we have name — in Domain, firstName & lastName

In RawUser we have phone number string — in Domain phone number is composed up of countryCode, areaCode, prefix and lineNumber

So we’ll need parsers in order to extract this info and also do so in a safe way (without crashes).

Extracting Person

Let’s look at the name, normally we’d write something like this to extract to first & last names from a string:

Do you see any problem with this implementation?

First off, the signature of the method is a lie! It doesn’t just return Person… it can throw can exception as well. Plus it’ll halt execution if an exception is encountered. How is that bad? Well consider this: If you have a million raw users and the first one has malformed data, the rest won’t be parsed either. This execution flow shouldn’t halt in such cases.

Solution: Don’t throw exception, instead return it — using the Either construct. With Either, we return Right in success case and Left in failure.

Now the method signature is kinda true as to what the method implementation does.

Extracting PhoneNumber

Ok the bad code first — using an incorrect regex (which doesn’t parse the last 4 digits correctly) to parse phone number and then assuming everything works:

Again — what’s wrong with this implementation: It’s not safe!

It can throw an exception on the last line but can also throw one if any of the toInt()’s fail too.

Solution:

  • Use Either so that the method returns either an exception or PhoneNumber
  • Use Try construct (also from Arrow) to create safeToInt() extension method on Strings, which returns Either an Int or an exception.
  • Use Either Monad Comprehension binding to resolve all safeToInt() either constructs within a block.

Ok first off — what is Either Monad Comprehension? It’s a binding block which resolves all inner Either constructs and gives us Left or Right as a final result. That’s why in this implementation, we return a binding block, which resolves all safeToInt()’s either constructs — returns Right(PhoneNumber) if successful and Left(Exception) if failed to get Int.

And also in the case where regex pattern doesn’t match, we return Left(Exception) for an incorrect format.

This implementation is now completely safe and handles all the different cases where the phone number string is not correct.

Creating DomainUser

Using a similar approach we’ll create a method for generating DomainUsers

This also uses Either Monad Comprehension and returns DomainUser only if both Person & PhoneNumber are generated successfully i.e. both are Right otherwise returns Left(Exception).

Printing the Results

If we execute the main method

fun main() {
generateRawUsers()
.map(::domainUserFrom)
.map { println(it) }
}

the output will be:

Right(
DomainUser(
person=Person(
firstName=Roth,
lastName=Drake
),
phoneNumber=PhoneNumber(
countryCode=1,
areaCode=230,
prefix=665,
lineNumber=4456
)
)
)
Left(
Exception: Can't extract first and last names from Andrew
)
Right(
DomainUser(
person=Person(
firstName=Kevin,
lastName=Kaufman
),
phoneNumber=PhoneNumber(
countryCode=1,
areaCode=609,
prefix=284,
lineNumber=788
)
)
)

The second user fails because it has no last name.

If the first user had an incorrect phone number, the output would become:

Left(
Exception: 1-230-665-A32 is not a number
)
Right(
DomainUser(
person=Person(
firstName=Roth,
lastName=Drake
),
phoneNumber=PhoneNumber(
countryCode=1,
areaCode=230,
prefix=665,
lineNumber=4456
)
)
)
Left(
Exception: Can't extract first and last names from Andrew
)

The chain itself never fails or halts execution.

--

--

Ahmed Rizwan
AndroidPub

Web | Android | Senior Software Engineer @QuixelTools