Advanced FP for the Enterprise Bee: Shiny Things

Garth Gilmour
Google Developer Experts
6 min readFeb 26, 2021
Vendor at a Honey Festival in France

Introduction

This is the final article in our eight part series. We have explored advanced Functional Programming for the curious, but practically minded, Kotlin developer. I hope you have found it worthwhile.

Here’s a review of everything covered to date:

In this final article I would like to:

  1. Show you some extra FP operations from Arrow
  2. Offer some insight into new features in Arrow 1
  3. Provide some links to resources I have found useful

Part 1: Additional Operations from Arrow

We have seen that Arrow can do an awful lot to simplify your day to day coding. But our survey was far from comprehensive. Here are some additional operations that I find useful in everyday development.

Making a Start

Most of our examples will begin with code similar to the below. We iterate over the range of integers between one and six and map them into either a Right or a Left:

[Left(1), Right(2), Left(3), Right(4), Left(5), Right(6)]

From Map to Bimap

We have already seen that map with Either is right-biased. Values inside a Left are ignored. But if you want to apply a transformation against both Left and Right then you can use bimap.

In the example below we multiply the odd numbers (contained within a Left) by ten and the even numbers (contained within a Right) by one hundred:

[Left(10), Right(200), Left(30), Right(400), Left(50), Right(600)]

Folding Revisited

In previous articles we used fold as a terminal operation. We supplied two side-effecting functions, in order to process Left and Right results. This could be as simple as result.fold(::println, ::println)

However this is not the only possible use of fold. Unlike bimap the fold operation returns a value instead of a container. So whereas applying bimap to an Either would produce another Either, applying fold produces a value.

Here is the same example, rewritten to use fold. As you can see the output is a list of numbers, rather than a list of Either:

[10, 200, 30, 400, 50, 600]

Fold comes in a number of interesting variations. For example bifoldLeft allows us to inject an additional value into the function.

Let’s use ten as our additional value. We add this value to the odd numbers whilst multiplying the even ones:

[11, 20, 13, 40, 15, 60]

Exchanging Values

In containers with two outcomes, it is possible to alternate between options via the swap operation. In this case all the Right items become Left, and vice versa:

[Left(1), Right(2), Left(3), Right(4), Left(5), Right(6)]
[Right(1), Left(2), Right(3), Left(4), Right(5), Left(6)]

Semigroups Revisited

In previous articles we saw that a Semigroup is a Container with a combine method. Most functional types are Semigroups, including Either.

The maybeCombine method combines two Either instances, provided they are both Left or Right. Here we will add one hundred to every Right instance in the list, but the Left instances will be unaffected:

[Left(1), Right(102), Left(3), Right(104), Left(5), Right(106)]

Applicatives Revisited

The second article in our series examined Applicatives in detail. Lists count as Applicatives and hence provide an ap operation.

If you provide a list containing a single function, then that function is applied to each item in the original list, as shown in result1 below. If you provide multiple functions then every function is allied to every item, as shown in result2:

[10, 20, 30, 40]
[100, 1000, 200, 2000, 300, 3000, 400, 4000]

Part 2: New Features in Arrow 1

When Arrow was first created, it was very much a volunteer driven effort to see if a library similar to Cats or the Haskell base library could be made to work in Kotlin. Happily the answer was yes! But some adjustments had to be made, given that Higher Kinded Types and Typeclasses were not intrinsic to Kotlin. See article three and article five in this series for more details.

As Arrow approaches the all important 1.0 release, the focus has shifted to maximising it’s usability and making the most of language features unique to Kotlin. This has resulted in some major changes. The most significant is that Higher Kinded Types will be dropped, for the foreseeable future until the Kotlin IDEA plugin is aware of community Kotlin compiler plugins.

As we have seen HKT’s require calls to fix, which are an impediment to learning. The only way to avoid these calls would have been via a compiler plugin and proper IDEA support. This could have inserted downcasts where required, without any manual intervention. Unfortunately, whilst Kotlin 1.4 provides a universal backend for all compilers, a standard API for compiler plug-ins has yet to emerge.

The good news is that a better alternative is already available. When you declare a function with suspend the compiler implements a continuation based state machine to handle the pausing and resumption of the function. My colleague Eamonn and myself wrote an article on this last year.

This mechanism can be used for more than concurrency with co-routines. Research has shown that suspending functions can be used to implement all the computation blocks over the types we have discussed in this series. So Either, Validated, State etc… relevant effect operators can be implemented as suspending functions, without HKT’s or Typeclasses.

This redesign, with a focus on suspension, will apply not just to Arrow Core, but also to Optics and Fx which by now provide inline versions of most popular FP operators to be used alongside suspension. With this change the IO type will be deprecated, given suspend functions cover all use cases of IO without the allocation and ADT costs.

You can read about this redesign in depth on the Arrow site. It offers substantial improvements in performance, due to the reduced need to allocate instances associated with Typeclasses, ADTs and abuse of the heap for these patterns to become stack-safe. The core types will also become easier to use - for example when calling traverse you will no longer need to pass in the associated Applicative instance.

All this new functionality will be arriving in two upcoming releases. Arrow 0.12 (already available in preview) will deprecate HKT’s and Typeclasses, whilst Arrow 0.13 will remove them entirely. Both of them will come out in the next few weeks, at the same time.

Users that are not using kinds, or don’t mind breaking changes, can go straight to 0.13. This is the release which will form the basis for Arrow 1.0, which is expected to be released this summer. It will be supported for the long run and easier industry adoption.

Part 3: Additional Resources

Here are some books, papers and talks which I particularly recommend. I’ve listed them in ascending order, based of the amount of FP knowledge the author(s) assume:

Conclusions

That’s it folks! I really hope you have found this series useful, and that it has helped you enhance your knowledge of FP concepts. Feedback is always welcome — both here, on Twitter and at work. Happy coding!

Thanks

My thanks to Richard Gibson and the Instil training team for reviews, comments and encouragement on this series of articles. Special thanks on this occasion to Raul Raja from 47 Degrees, for chatting with Richard and myself about their vision for Arrow 1. All errors are of course my own.

--

--

Garth Gilmour
Garth Gilmour

Written by Garth Gilmour

Helping devs develop software. Coding for 30 years, teaching for 20. Technical Learning Consultant at Liberty Mutual. Also martial arts, politics & philosophy.

No responses yet