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

Added orElse combinator to IO #2940

Merged
merged 5 commits into from
Apr 6, 2022
Merged

Conversation

walesho
Copy link
Contributor

@walesho walesho commented Apr 4, 2022

Added orElse combinator to IO.

Example of Usage
val firstIO = IO.raiseError[Int]
val secondIO = IO.pure(42)
val program = firstIO orElse secondIO // this would result to secondIO

@rossabaker
Copy link
Member

This is what the MonoidK instance does for combineK. I think it's a useful enough operation to give it a name like this.

Comment on lines 460 to 461
def orElse[B >: A](other: IO[B]): IO[B] =
handleErrorWith(_ => other)
Copy link
Contributor

Choose a reason for hiding this comment

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

If it just calls to handleErrorWith then (perhaps) it would be better to add such a method directly to Cats' ApplicativeError type class instead of just IO. Because apparently it would make orElse functionality uniformly available for all ApplicativeError implementers (which includes IO from Cats Effect 2.x, Monix Task, fs2.Stream, etc). Wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

It's already syntax on ApplicativeError. I'm not sure why it shouldn't also be on the type class.

Regardless, I think the trend on IO has been toward a rich interface without typeclass syntax. Adding it here is consistent with Either, EitherT, etc.

Copy link
Contributor

@satorg satorg Apr 5, 2022

Choose a reason for hiding this comment

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

I'm not sure why it shouldn't also be on the type class.

I'm not sure either. I wonder if there is any convention on that in general. The syntax was introduced in typelevel/cats#2243 but seems the option for adding it to the typeclass had not been discussed nor challenged.

In general, having a method on typeclass makes it possible to override it in a particular implementation (e.g. for the sake of optimization). Not sure if it may make sense to do that for this orElse though.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's already syntax on ApplicativeError.

Btw, the syntax for ApplicativeError is defined in this way:

  def orElse(other: => F[A])(implicit F: ApplicativeError[F, E]): F[A] =
    F.handleErrorWith(fa)(_ => other)

Notice it takes other as a by-name param while the suggested addition to IO takes it as just a value.

Wouldn't it be better to keep these two methods in correspondence to each other? I.e. to declare the latter's other as a by-name parameter too? @rossabaker @djspiewak , wdyt?

Copy link
Member

Choose a reason for hiding this comment

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

Right, good catch. This should definitely be a by-name.

Copy link
Member

Choose a reason for hiding this comment

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

Btw, it's really OOPy but if we had inheritable syntax traits for the monad classes themselves, couldn't we get all this syntax for free and avoid oopsies? Similar to how I've suggested that the IO companion object itself should implement Async[IO].

Copy link
Member

Choose a reason for hiding this comment

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

So the by-name parameter point is a really important one. If this already exists as syntax on ApplicativeError, then we need to be consistent with that.

Copy link
Member

@djspiewak djspiewak left a comment

Choose a reason for hiding this comment

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

This is great! Can you please run sbt prePR though? Additionally, as @rossabaker pointed out, these are the same semantics as MonoidK[IO], so it might be worth adjusting that instance to delegate to orElse to avoid duplication and improve test coverage.

@@ -458,7 +458,7 @@ sealed abstract class IO[+A] private () extends IOPlatform[A] {
* @return
*/
def orElse[B >: A](other: IO[B]): IO[B] =
handleErrorWith(_ => other)
SemigroupK[IO].combineK(this, other)
Copy link
Member

Choose a reason for hiding this comment

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

I would go the other way around: leave this like you had it, and implement SemigroupK in terms of it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the input. I would follow that approach.

Comment on lines 460 to 461
def orElse[B >: A](other: IO[B]): IO[B] =
handleErrorWith(_ => other)
Copy link
Member

Choose a reason for hiding this comment

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

So the by-name parameter point is a really important one. If this already exists as syntax on ApplicativeError, then we need to be consistent with that.

@walesho walesho requested a review from djspiewak April 5, 2022 21:41
Copy link
Member

@djspiewak djspiewak left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@armanbilge
Copy link
Member

CI failure is because series/3.x is behind series/3.3.x so MiMa is rightfully upset :) merging #2947 should fix this, I'll look into that now.

@armanbilge
Copy link
Member

Closing / re-opening to trigger CI on a fresh merge commit, apologies.

@armanbilge armanbilge closed this Apr 6, 2022
@armanbilge armanbilge reopened this Apr 6, 2022
@armanbilge
Copy link
Member

Congrats on your first PR! @walesho 🎉

@armanbilge armanbilge merged commit eb94112 into typelevel:series/3.x Apr 6, 2022
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.

5 participants