Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants