-
Notifications
You must be signed in to change notification settings - Fork 65
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
Convenience functions for lifting to OptionT & EitherT #260
Conversation
Hi @henryxparker Needs clearer motivation : can you include an example usage demonstrating how these extensions make user code cleaner or more concise? |
Sure! let's say I'm writing a simple program that does a few things but I need to recover from failures. it would look like def program =
for {
requestedThing <- EitherT(getRequestedThing.attempt)
id <- EitherT.fromOption[IO](parseId(requestedThing.idString), invalidIDError)
...
} yield result The first thing you read for each line is a bulky def program =
for {
thing <- getThing
id <- parseId(thing.idString)
...
} yield result Now we have a nice semantic program, but really I've only pushed it down a bit def getTheThing: IO[A]
def validate(a: A): Either[E,A]
def getAThingAndValidate: EitherT[IO, E, A] =
EitherT(
getTheThing.map(validate)
) My example was admittedly small enough to fit onto one line, but it's not hard to imagine a scenario with lots of I don't think that's a particularly bad thing in most cases, but in this case I find the following notation much neater def getAThingAndValidate: EitherT[IO, E, A] =
getTheThing
.map(validate)
.liftToEitherT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Syntaxes for F[Option[A]]
and F[Either[L, R]]
are aimed to not deal with monad transformers. I'm convinced that methods could be replaced with constructors (smart constructors) of monad transformers themselves.
@@ -25,4 +28,6 @@ final class OptionOps[A](private val oa: Option[A]) extends AnyVal { | |||
* `Serializable with Product with Either[A, B]` | |||
*/ | |||
@inline def left[B](b: => B): Either[A, B] = oa.toLeft(b) | |||
|
|||
@inline def toOptionT[F[_]: Functor]: OptionT[F, A] = OptionT.liftF(oa) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cats
contains the same method in the Option
syntax https://github.com/typelevel/cats/blob/main/core/src/main/scala/cats/syntax/option.scala#L368
That's one reason why unit tests matter even for simple methods; they're testing the compiler types.
|
I added some tests, formatted, and removed everything except |
Thanks for your contribution @henryxparker |
Adding some extension methods that I often wish I had. It really boils down to code cleanliness preferences.
I just wanted to get some feedback on whether others feel like these conversions belong here, or if they're fundamentally anathema/broken in some non-obvious way.
If others are amenable then I will write some tests and correct the formatting