typelevel / mouse

A small companion to cats
MIT License
371 stars 66 forks source link

Add recover either #506

Open geirolz opened 4 months ago

geirolz commented 4 months ago

Add methods to AnyF to recover errors to Either where an instance of MonadError[F] is provided.

New methods:

satorg commented 4 months ago

Thank you for your contribution!

However, let me chime in and share my thoughts about some of the PR details please.

  1. MonadError does not seem necessary here. ApplicativeError should be just enough (unless I missed something out).
  2. recoverEither with all the syntax enabled seems to be a shortcut for:
    fa.recoverEither(pf) <=> fa.mapAsRight.recover(pf)

    I wonder is there a benefit from introducing a function that can be represented as a chain of other two existing functions without adding any new functionality of its own?

  3. recoverAsRight might seem more sophisticated but in fact it can be expressed with the same two functions but chained in the reverse order:
    fa.recoverAsRight(pf) <=> fa.recover(pf).mapAsRight

    But the latter requires less transformations to be applied - is it correct?

Note that I cannot find a simple replacement for recoverAsLeft. Therefore it looks like the most worthwhile addition to me (even though it could be a quite narrow use case).

geirolz commented 4 months ago

Thank you @satorg for the detailed review!

  1. MonadError does not seem necessary here. ApplicativeError should be just enough (unless I missed something out).

You are right! Updated the PR

  1. recoverEither with all the syntax enabled seems to be a shortcut

Right, I added it for completeness mainly - the true need was just recoverAsLeft. Btw many operators implementation are just shortcut so I wouldn't care to much about it. The scope of mouse is to provide a more easily and convenient syntax

  1. recoverAsRight might seem more sophisticated but in fact it can be expressed with the same two functions but chained in the reverse order.

Didn't notice that - you are right!

recoverAsLeft is the main reason for this PR. Happens that you have an effect that contains the errors, some of them are failures for you and you want to move them in the left branch of the Either. In this way is like an attempt but only for some cases defined by the partial function.

val ioa: IO[Unit] = ???
ioa.recoverAsLeft { 
 case ex: MyLogicalFailure => ex
}

// or when you have to lift to Either only some errors and map them basically 
// In this case the errors not mapped will raise an exception and will be treated as 500 ( internal server error ) - the others // are properly treated with different error codes 
ioa.recoverAsLeft { 
 case ex: MyLogicalFailure => HttpError(400, ex.getMessage)
}