Up front: I know how to just write a custom function that will do this, but I swear there's a built-in thing whose name I'm just forgetting, to handle it in an idiomatic way. (Also, in my actual use case I'm likely to be using more complex monads involving state and assorted nonsense, and I feel like the answer I'm looking for will handle those as well, while the hand-coded one would need to be updated.)
I have a list items : List[A] and a function f : (A) -> Either[Error, B]. I vaguely recall there's an easy dedicated function that will apply f to each item in items and then return the first Right(b) to result, without applying f to the remaining items (or return Left[error] of the last error, if nothing succeeds.)
For example, if you had f(items(0)) result in Left("random error"), f(items(1)) result in Right("Find this one!"), and f(items(2)) result in launchTheNukes(); Right("Uh oh."), then the return should be Right("Find this one!") and no nukes should be launched.
It's sort of like what's happening in a for comprehension, where you could do:
for{
res0 <- f(items(0))
res1 <- f(items(1))
res2 <- f(items(2))
} yield res2
Which would return either the first error or the final result - so I want that, but to handle an arbitrary list rather than hard-coded, and returning the first success, not the first error. (The answer I'm looking for might be two functions, one to swap the sides of an Either, and one to automatically chain foldLefts across a list... I think there's a single-step solution though.)
Code snippet for commented solution:
def tester(i : Int) : Either[String, Int] = {if (i % 2 == 0) Right(100 / (4 - i)) else Left(i.toString)}
(1 to 5).collectFirst(tester)
I'm assuming (from your mention of more complex monads such as State) that you're using the Cats library.
You probably want one of the methods that come from
TraverseFor example, its
sequenceandtraversemethods are two variations of the "I have a list of things, and a thing-to-monad function, and I want a monad of things". Since Either is a monad whoseflatMapaborts early upon encountering a Left, you could.swapyour Eithers so that theflatMapaborts early upon encountering aRight, and then.swapthe result back at the end.