We’re excited to announce Arrow 1.2.0-RC alongside a new Arrow website. This release introduces a brand new typed errors module, and adds more options for resilience.
We also move forward on our way to 2.0: we’ve marked every function and type we intend to remove as @Deprecated
to gather input from the community before the actual removal. Note that those decisions are not yet final. One of our aims with the 1.2.x series is to gather input from the community, and we’re very happy to hear about the impact of these changes on your projects.
New website
The Arrow website has been growing organically during the last few years. The documentation there was pretty complete but challenging to find at times. Arrow getting closer to 2.0 was a good moment to refresh the website, with the double goal of making first-time users feel more welcome into the ecosystem while giving power users a fast track to the information they might need.
The result is a brand new Arrow website with a strong focus on guides and tutorials. We’ve also refreshed the API docs, which provide direct and concise information. The team at Xebia Functional has supported us with a new modern, streamlined design.
The new website is being developed in the open. We’d love to hear (or merge a PR) if you have any ideas, suggestions, or issues. In particular, if you’re using Arrow in your daily job or side project, we’re collecting examples and use cases that may help other developers get started on their Arrow journey.
Typed errors
Typed errors are the main technical addition to this Arrow release. Under that abstract name, we refer to a new approach to working with error types, which replaces our previous computation blocks and the Effect
/EagerEffect
types.
If you’re already using either
, result
, or any other type of computation blocks, you should feel at home with typed errors. Within one of these blocks, you get the ability to raise
errors or to embed other possibly-failing computations with bind
.
fun validPerson(name: String, age: Int): Either<UserProblem, User> = either {
ensure(name.isNotEmpty()) { UserProblem.EmptyName }
val validatedAge = validAge(age).bind()
User(name, validatedAge)
}
We’ve improved the interface for error accumulation, which no longer requires different types (as with Either
and Validated
in the past). You can use mapOrAccumulate
and zipOrAccumulate
in any block to "switch away" from fail-fast mode.
fun validPerson(name: String, age: Int): Either<NonEmptyList<UserProblem>, User> = either {
zipOrAccumulate(
{ ensure(name.isNotEmpty()) { UserProblem.EmptyName } },
{ validAge(age).bind() }
) { validName, validAge-> User(validName, validAge) }
}
The dichotomy between "eager" and "regular" effects is also gone in the new typed errors framework. The same API works for both suspend
ed and non-suspend
ed scenarios.
More resilience
The resilience module now includes support for sagas. Sagas handle the case in which several operations spanning different microservices must succeed or fail as a unit; otherwise, we may end up in an inconsistent state. This is achieved by declaring a compensating action for each operation; in the example below, we try to increment a counter but decrement it if the operation fails.
saga({
Counter.increment()
}) {
Counter.decrement()
}
Similar to what Arrow does with Resource, the implementation guarantees that compensating actions are executed when expected by the rules of Structured Concurrency.
We plan to double our efforts on this front. We’ve split the resilience types to a new arrow-resilience
module, which gives us more freedom to iterate faster than the rest of Arrow, for which stability is a must.
Road towards 2.0
Arrow 1.2.0 lays down all the work to help you migrate to the forthcoming 2.0 release. We’ve marked every function we intend to remove in the new major version as @Deprecated
. In most cases, we provide direct replacements using ReplaceWith
, which translates to quick fixes in IDEs like IntelliJ, and for more global changes, we provide OpenRewrite scripts. Everything is explained in the migration guide for this release. If your code compiles without deprecation warnings in 1.2.0, it should be fully source-compatible with 2.0.
To give an overview of the most important deprecations:
Validated
is being replaced, to have a singleEither
type to represent logical errors. The error accumulation facilities inValidated
have been subsumed bymapOrAccumulate
andzipOrAccumulate
.- The typed errors mechanism replaces
effect
and computation blocks. - We’ve continued the reduction of the API surface of many types. One crucial example is
traverse
, which is being deprecated in favor of using regularmap
within a typed error block. Semigroup
andMonoid
are leaving us too in Arrow 2.0. We’ve decided to align with Kotlin’s practice of having the empty element and combination function as parameters, like infold
.
We reiterate that we’re planning an extended grace period before Arrow 2.0 is released. We have no fixed schedule at this point since some of the coming changes in Kotlin itself may further influence the direction. During this period, we’ll keep releasing modules in the 1.2.x series fixing problems, improving the API, and further helping with the migration process.
Xebia 💙 Kotlin
We, the functional team at Xebia, are great fans of Kotlin, exploring the many possibilities it brings to the back-end scene. We’re proud maintainers of Arrow, a set of companion libraries to Kotlin’s standard library, coroutines, and compiler; and provide Kotlin training to become an expert Kotliner. If you’re interested in talking to us, you can use our contact form, or join us on the Kotlin Slack.