Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arrow 2.0 release notes #312

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
180 changes: 180 additions & 0 deletions content/blog/2024-07-01-arrow-2-0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
---
title: Arrow 2.0 release
category: articles
tags: [core, articles]
---

# Arrow 2.0 release

We are happy to announce the next major release of Arrow, version 2.0!
As previously announced, migrating your projects to this release should be hassle-free
if your code compiled in 1.2.x without any deprecation warnings
(except for the breaking change in optics generation discussed below).

This release is built with the new K2 compiler, and this gives us the ability
to support a wider range of platforms, including WebAssembly. From now on, we shall
provide artifacts for every platform supported by Kotlin.

Apart from stabilization and general bug fixing, the theme of this release
is improving the different DSLs provided by Arrow libraries. Our goal is to
empower developers to write more succinct and readable code.

* [Simple accumulation in Raise](#simple-accumulation-in-raise)
* [Additions to Fx](#additions-to-fx)
* [Clearer retries for particular exceptions](#clearer-retries-for-particular-exceptions)
* [Improved optics](#improved-optics)
* [Pattern matching](#pattern-matching)
* [Better support for kotlinx.serialization](#better-support-for-kotlinxserialization)

## Simple accumulation in Raise

One of the core concepts when working with typed errors is the distinction
between fail-first and accumulation of errors. Until now, the latter mode
required using `parZip` and `parMap`, which sometimes obscure the actual
flow of the computation.

In Arrow 2.0 we have sprinkled some DSL dust over `Raise`, and now you can
write your code in a more linear way. Inside an `accumulate` block (or in
general, any `RaiseAccumulate`) you use `by accumulating` to execute some
computation keeping all the errors.

```kotlin
// version with parZip
parZip(
{ checkOneThing() },
{ checkOtherThing() }
) { a, b -> doSomething(a, b) }

// version with accumulate
accumulate {
val a by accumulating { checkOneThing() }
val b by accumulating { checkOtherThing() }
doSomething(a, b)
}
```

This DSL also includes shortcuts for the most common operations, like
`bind`ing and accumulating any problem, or checking a single property
of some data.

```kotlin
accumulate {
val name by Name(rawName).bindOrAccumulate()
val age by ensureOrAccumulate(rawAge >= 18) { UnderAge }
Person(name, age)
}
```

## Additions to Fx

Writing coroutine-heavy code may become cumbersome over time, especially if
one intends to use as much concurrency as possible. Arrow Fx includes a `parZip`
function, but not everybody enjoys having so many brackets.

```kotlin
parZip(
{ downloadFile() },
{ loadDataFromDatabase() }
) { file, data -> Result(file, data) }
```

The new `awaitAll` scope tries to improve the situation by tweaking the
usual `async` mechanism, ensuring that all `Deferred` values are `await`ed
once the first one is requested. That means that the previous code behaves
identically to the following, that is, the call `file.await()` implicitly
awaits every `async` defined up to that point.

```kotlin
awaitAll {
val file = async { downloadFile() }
val data = async { loadDataFromDatabase() }
Result(file.await(), data.await())
}
```

We've also improved the STM block by allowing delegation as a means to
read or change the value of a `TVar`.

```kotlin
fun STM.deposit(accVar: TVar<Int>, amount: Int): Unit {
val acc by accVar // delegation here
val current = acc // implicit 'read'
acc = current + amount // implicit 'write'
}
```

## Clearer retries for particular exceptions

Until now, the `retry` operation in the Resilience module would capture
any `Throwable` exception. From version 2.0 on you can specify a subclass
of `Throwable` to be the target for retrying, whereas the rest of
exceptions will bubble as usual.

```kotlin
Schedule.recurs<Throwable>(2)
.retry<IllegalArgumentException, _> { ... }
```
Comment on lines +113 to +116
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment, I think it is important we can call doWhile on IllegalArgumentException here as well.


The subclass of exceptions must be given as a type argument.
Alas, Kotlin does not allow giving only a subset of those, and `retry`
has two type parameters (the second one represents the output type of
the `Schedule`). Fortunately, you can ask the compiler to infer the
second one using `_`.

## Improved optics

The two **breaking changes** in Arrow 2.0 relate to optics.
First of all, the optics hierarchy has been greatly simplified:
now we have traversals, optionals, lenses, prisms, and isos, and no more
intermediate types. This smaller amount of types means that the type of
optic compositions become easier to understand.

We have also changed the generation of optics via the compiler plug-in
(that is, the `@optics` annotation) with respect to nullable fields.
In the 1.x series, a value of type `String?` would be presented as
`Optional<T, String>`; this makes impossible to change the value from
`null` to an actual `String` using only optics operations. From version
2.0, that field is represented as `Lens<T, String?>`. To get the 1.x
behavior you should apply `.notNull` after the optic corresponding to
the field.

One pain point when building traversals was the need to provide an
argument to `.every`, like `.every(Every.list())`. This new version
brings an improved variant that requires no arguments if the type
of the `Iterable` is known. Similar improvements have been applied
to `.at` and `.index`.

## Pattern matching

One completely new feature in Arrow 2.0 is the _pattern matching_ DSL.
By combining prisms and lenses one can specify a complex shape, and
then check whether a value fits into that shape, extracting some
pieces of information on the go. The DSL gets quite close to pattern
matching found in functional languages like Haskell or Scala.

We do not intend this package to be a replacement for `when` expressions,
smart casts, and guards already provided by the language. On the other
hand, we acknowledge that pattern matching offers advantages when the
code needs to inspect very nested data.

```kotlin
val User.name: String get() = this.matchOrThrow {
// Company(name = nm, director = Name(lastName = d))
User.company(Company.name, Company.director(Name.lastName)) then { (nm, d) -> "$nm, att. $d" }
// Person(Name(firstName = fn), age if it < 18)
User.person(Person.name(Name.firstName), Person.age.suchThat { it < 18 }) then { (fn, _) -> fn }
// Person(Name(firstName = fn, lastName = ln)) -> "Sir/Madam $fn $ln"
User.person(Person.name(Name.firstName, Name.lastName)) then { (fn, ln) -> "Sir/Madam $fn $ln" }
}
```

We also provide a new package, `arrow-match`, which provides the same
pattern matching DSL, but using Kotlin's reflection instead of optics.

## Better support for kotlinx.serialization

Using Arrow Core data types as part of serialized data requires additional integration.
In 1.2.x we started providing compile-time support for `kotlinx.serialization`.
From 2.0 on we also provide `ArrowModule` for
[contextual serialization](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization). This is needed, among others, when the data is processed
by Ktor.
12 changes: 12 additions & 0 deletions content/docs/learn/resilience/retry-and-repeat.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ steps involved in using `Schedule`.
It returns the last internal state of the scheduling policy
or the error that happened running the action.

:::tip Retrying only on certain exceptions

Since version 2.0, you can use specify a subclass of `Throwable` as first type argument
to `retry` to focus only on those exceptions. It is customary to leave the second type
argument unspecified.

```kotlin
policy.retry<IllegalArgumentException, _> { ... }
```

:::

## Constructing a policy

<!--- TEST_NAME RetryRepeat -->
Expand Down
Loading