Skip to content

Conversation

@DatL4g
Copy link

@DatL4g DatL4g commented Dec 17, 2025

This adds the possibility to destruct Either and Ior like in go:

val (err, data) = Either.catch { mayThrow() }

Unfortunately I can't run apiDump because it can't find the task and build as it fails with the following error:

Could not determine the dependencies of task ':arrow-core:animalsnifferAndroidMain'.
> Task with name 'androidMainClasses' not found in project ':arrow-core'.

However any maintainer can quickly jump in run and commit these in this PR.

@kyay10
Copy link
Collaborator

kyay10 commented Dec 17, 2025

apiDump is now updateLegacyApi. We should document this somewhere...
I don't know quite how to feel about this API. I know this is a popular pattern in Go. I'm not sure, though, if it fits well with Kotlin.
With contracts, maybe it ends up being idiomatic? I'd have to play around a bit with it...

…nents

Includes build configuration change to allow contracts on operators.
@serras
Copy link
Member

serras commented Dec 20, 2025

To be honest, I don't think this fits the style we want to promote in Arrow. What are your use cases and why doesn't using a when, or the functions fold, onRight/onLeft, and so on work for you? Adding this means people may accidentally destructure during lambdas ... { (err, data) -> ... }, and this may be quite a surprising mechanism.

Also, note that using A? is not great, because you can potentially lose information about what branch you took. For example, if you have Either<Problem, User?>, then by using component2 you get User? again, so you don't really know what went on.

@DatL4g
Copy link
Author

DatL4g commented Dec 20, 2025

I can't share my exact use-case unfortunately, however our main focus isn't really on Either but on Ior.

The main point on adding it to Either was providing consistent API behavior in comparison to Ior, as well as to support developers coming from languages where this linear style is standard.

Use Case: Resilient Parsing / Success with Warnings

When using Ior to model "Partial Success" (e.g. parsing a document where some fields are malformed but others are recoverable), consuming the result is currently painful.

Current State

To simply log the errors and use the data, we have to handle all 3 branches (Left, Right, Both), often duplicating logic:

result.fold(
	{ err -> logger.warn(err) },
	{ data -> process(data) },
	{ err, data -> 
		logger.warn(err)
		process(data)
	}
)

This boilerplate might look acceptable at first glance, but as logic grows, this approach creates an unavoidable pyramid of doom.

Destructuring

The descructuring approach fits the semantic of "inclusive or" perfectly I think because it allows independent handling of the independent axes.

val (problems, value) = docParser.parse(input)

if (problems != null) {
	logger.warn("Parsing issues found: $problems")
}

if (value != null) {
	process(value)
}

This flattens the control flow and decouples the error handling from the success handling, which is exactly what Ior is designed to support.

Regarding fold, map, when etc

As mentioned earlier, these examples aren't really problematic but as the code-base grows we simply need Guard Clause from time to time, without nesting deeper and deeper to preserve maintainability.

@kyay10
Copy link
Collaborator

kyay10 commented Dec 20, 2025

You should have a look at the iorAccumulate DSL, it might serve your use case well! Your logging is akin to error accumulation, hence why iorAccumulate might be nice.

@DatL4g
Copy link
Author

DatL4g commented Dec 20, 2025

Yeah we're already using iorAccumulate at some points but it doesn't always fit our use-cases since we don't want to keep the warnings.

Anyway I understand all your concerns and if you decide against this.

@kyay10
Copy link
Collaborator

kyay10 commented Dec 20, 2025

It'd be great to hear what shortfalls you find with iorAccumulate! I'm not sure what you mean by "keep the warnings"?

@serras
Copy link
Member

serras commented Dec 20, 2025

I still see this as a no-go for the current API design of Arrow. Although there may be situations where this may help, making this part of the library brings a lot of potential for misuse. A few pointer, though.

I don't see how your example gets much worse with matching:

val result = docParser.parse(input)

if (result is Left) {
	logger.warn("Parsing issues found: ${result.value}")
}

if (value is Right) {
	process(result.value)
}

In fact, for Either we provide a few combinators specifically for "do some small side-effectful thing and keep going"

val result = docParser.parse(input).onLeft { 
  logger.warn("Parsing issues found: ${result.value}")
}

@hoc081098
Copy link
Contributor

Either is a union type. I think we should not treat it as a product type.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants