Skip to main content

Arrow 2.2.0

· 4 min read

Arrow 2.2.0 is out, with improved and new functionality. This release builds on top of several new features in Kotlin 2.2, in its self-imposed role of perfect companion for your Kotlin journey.

Raise with context parameters. The new package arrow.core.raise.context provides the same API as arrow.core.raise, but using context parameters instead of extension functions. This package is intended to be a full 1-1 replacement the extension-based API.

Unfortunately, currently mixing Raise functions using extension-style and context-style doesn't lead to the better experience. Our advice is to fully migrate to the context-style API if possible, or stay completely within the extension style.

// extension-style
fun Raise<PersonValidationError>.buildPerson(name: String, age: Int): Person { ... }

// context-style, "fake" constructor
context(Raise<PersonValidationError>)
fun Person(name: String, age: Int): Person { ... }
Enabling context parameters

In order to define functions with context parameters, you need to enable the corresponding language feature, as described in the Kotlin documentation.

New Racing DSL for concurrency. Arrow Fx provides high-level concurrency combinators to succinctly describe how tasks should be interleaved in a computation. Albeit useful, those combinators require nesting and writing your code in a particular way. In Arrow 2.2.0 we introduce a new Racing DSL. Combined with the already existing AwaitAll DSL, you can write your coroutines code with little to no changes, while using as much concurrency as possible during execution.

validate for error values. We have introduced a small utility function to bridge the world of validation functions working on Raise, and chained style using Either or Option. This function is called validate, and it should be commonly used in conjunction with ensure and similar functions.

fun failOnMoreConditionsWithBindMap(): Either<String, Int> =
randomNumber()
.validate { ensure(it != 10) { "Number 10 also not allowed" } }
.map { it + 100 }

This new function provides no functionality you could not get before. For example, here is the code equivalent to the one above, but using the Either DSL.

fun failOnMoreConditionsWithBindMap(): Either<String, Int> = either {
val random = randomNumber().bind()
ensure(random != 10) { "Number 10 also not allowed" }
random + 100
}

We acknowledge that the chain-of-calls style is a common one, and we want the Arrow API to appeal to users using their preferred style.

"At most once" for Eval. Arrow provides the Eval type for fine-grained control over evaluation. This is similar to lazy in the standard library, but you can choose between eager, lazy, and repeated (computed every time) evaluation strategies. However, there was no (easy) way to ensure that a computation was evaluated at most once.

The new Eval.atMostOnce function (alongside SuspendEval.atMostOnce) provides this new behavior. You should be aware, though, that this means that getting the value of an Eval built this way may block (or suspend) if more than one thread does so concurrently.

Changes to toString for non-empty collections. After a long discussion in Slack it became clear that the behavior of toString for non-empty collections should just reflect that of the wrapped collection. Until now, using NonEmptyList or NonEmptySet added a prefix — from now on, we treat non-emptiness as simply a property of the collection, no different than other such as their size, that are not reflected when turning into a string.

New arrow-core-result4k module. Result4k is a popular library for computation that may succeed or fail. We now provide support for it as part of typed errors. You can either consume them — that is, .bind() a Result value — or produce them — use the result4k builder in a similar way to either.

Arrow Optics for Gradle, beta. We have been working on a new approach to handle @optics annotations with much less configuration, and that removes the need for manually writing companion objects. This new approach is in beta, read here all the details.

Migration to new plug-ins. Following our eager dependency update policy, we have moved to the new recommended plug-ins in the ecosystem.