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

Add EitherT and IorT constructors from Option with monad left value #3457

Conversation

ivan-klass
Copy link
Contributor

@ivan-klass ivan-klass commented Jun 6, 2020

This is to follow-up on feedback for #3426

@kailuowang I'm trying to understand the idea in your feedback comment

Option, like several other data types, already has a liftTo[F] that can "lift" it into any F[_] with an ApplicativeError[F], which is the case for both IorT and EitherT.
For lifting to the left side, I believe we had a PR adding a raiseTo[F] but somehow I couldn't find it anymore.
I am thinking that instead of adding n - 1 methods to n datatypes to support transformation between we can just add a liftTo[F] and a raiseTo[F] to n data types?
In this example, we can add those to OptionT and F[Option]

update: I found the raiseTo PR #3358

Do you mean that we can add something like

val foa = F[Option[A]]

def orRaiseF[G[_], E](fe: => F[E])(implicit F: Monad[F], A: ApplicativeError[G, E]): F[G[A]] =
  foa.flatMap { 
     case Some(a) => F.pure(A.pure(a))
     case None => fe.map(A.raiseError)
  }

foa.orRaiseF[Either[Err, *], Err](fErr)
foa.orRaiseF[Ior[Err, *], Err](iorErr)

?

There could be partially applied helper objects to simplify type-hints - so we only have to provide G[_] manually and E could be inferred from argument

Did I get the idea right? Thanks

@codecov-commenter
Copy link

Codecov Report

Merging #3457 into master will increase coverage by 0.01%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master    #3457      +/-   ##
==========================================
+ Coverage   91.60%   91.61%   +0.01%     
==========================================
  Files         382      382              
  Lines        8311     8321      +10     
  Branches      226      223       -3     
==========================================
+ Hits         7613     7623      +10     
  Misses        698      698              

@kailuowang
Copy link
Contributor

Sorry, now I read it again, my last comment was not really clear. Let me try again.
Your use case is to turn a foa: F[Opiton[A]] into an EitherT or IorT
You can take advantage of the liftTo on Option

val foa: F[Option[A]] = ???
foa.map(_.liftTo[M](e))

This gives you a F[M[A]], ultimately you want a M[A]. We can write help methods for specific M[_] types to for that purpose. For example, We can write helper syntax method for
F[EitherT[F, A, B]] to easily turn into EitherT[F, A, B] (maybe call it flattenEitherT)

If we find foa.map(_.liftTo[M](e)) too verbose, we add a helper syntax method liftTo method to F[Option[A]] so that you can write
foa.liftTo[M](e) which gives you a F[M[A]]

Then with these two helper syntax methods combined, you can write

foa.lfitTo[EitherT[F, E, *][(e).flattenEitherT

which turns F[Option[A] to EitherT[F, E, A]

On other side, we can add a raiseTo to Option by inheriting #3358.
Then we can write

foa.map(_.raiseTo[M][(defaultValue))

which turns a F[Option[E]] into a F[M[A] given a default value A if the Option is empty.

Note that we are using E instead of F[E], which is what ApplicativeError commonly work with. If you need F[E] and avoid F[E] being evaluated, these methods don't apply.

Please take this as a starting point of a discussion, let me know what you think.

@ivan-klass
Copy link
Contributor Author

ivan-klass commented Jun 9, 2020

@kailuowang

Note that we are using E instead of F[E], which is what ApplicativeError commonly work with. If you need F[E] and avoid F[E] being evaluated, these methods don't apply.

That's the point, right. There are simple ways to provide a plain error but my case is not quite

turn a foa: F[Opiton[A]] into an EitherT or IorT

but

(F[Opiton[A]], F[E]) => EitherT[F, E, A]

or better with call-by-name

(foa: F[Opiton[A]], fe: => F[E]) => EitherT[F, E, A]

Copy link
Contributor

@kailuowang kailuowang left a comment

Choose a reason for hiding this comment

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

@ivan-klass ah I apologize, I missed "may also require the left to be calculated as F[E]. " from your original PR.
This PR looks good to me as is now.

@kailuowang kailuowang requested a review from LukaJCB June 10, 2020 14:23
Copy link
Member

@LukaJCB LukaJCB left a comment

Choose a reason for hiding this comment

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

Thanks for bearing with us! :)

@LukaJCB LukaJCB merged commit c716e06 into typelevel:master Jun 11, 2020
@travisbrown travisbrown added this to the 2.2.0-M3 milestone Jun 12, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants