From 116718be314e574c33cdddcde0e2aba40ee9490a Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Wed, 29 May 2024 21:26:26 +0200 Subject: [PATCH 1/3] Arrow 2.0 release notes --- content/blog/2024-07-01-arrow-2-0.md | 115 +++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 content/blog/2024-07-01-arrow-2-0.md diff --git a/content/blog/2024-07-01-arrow-2-0.md b/content/blog/2024-07-01-arrow-2-0.md new file mode 100644 index 00000000..914e099e --- /dev/null +++ b/content/blog/2024-07-01-arrow-2-0.md @@ -0,0 +1,115 @@ +--- +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 + +TODO: Talk about `accumulating` + +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 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. + +## 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. + +```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, 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(2) + .retry { ... } +``` + +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`; 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`. 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`. + +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. The +interested reader may check the documentation. From a58845f921307c9317cedef272325afe7f41e5d0 Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Tue, 1 Oct 2024 16:57:44 +0200 Subject: [PATCH 2/3] Talk about accumulating --- content/blog/2024-07-01-arrow-2-0.md | 83 +++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/content/blog/2024-07-01-arrow-2-0.md b/content/blog/2024-07-01-arrow-2-0.md index 914e099e..74beec26 100644 --- a/content/blog/2024-07-01-arrow-2-0.md +++ b/content/blog/2024-07-01-arrow-2-0.md @@ -19,15 +19,51 @@ 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 -TODO: Talk about `accumulating` +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. -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 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. +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 @@ -45,7 +81,8 @@ parZip( 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. +identically to the following, that is, the call `file.await()` implicitly +awaits every `async` defined up to that point. ```kotlin awaitAll { @@ -107,9 +144,37 @@ 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. The -interested reader may check the documentation. +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. From de555b169a9824060724fb6ca6467d2c4f9d813d Mon Sep 17 00:00:00 2001 From: Alejandro Serrano Date: Thu, 3 Oct 2024 09:11:10 +0200 Subject: [PATCH 3/3] Add information about new retry --- content/docs/learn/resilience/retry-and-repeat.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/content/docs/learn/resilience/retry-and-repeat.md b/content/docs/learn/resilience/retry-and-repeat.md index 906e6f36..faeb922c 100644 --- a/content/docs/learn/resilience/retry-and-repeat.md +++ b/content/docs/learn/resilience/retry-and-repeat.md @@ -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 { ... } +``` + +::: + ## Constructing a policy