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

Bracket for MTL #135

Open
iRevive opened this issue Aug 5, 2019 · 2 comments
Open

Bracket for MTL #135

iRevive opened this issue Aug 5, 2019 · 2 comments

Comments

@iRevive
Copy link
Contributor

iRevive commented Aug 5, 2019

Hi all.

Bracket typeclass, that exists in cats-effect, doesn't work well with an effect that relies on FunctorRaise.
I want to implement a clean-up logic in case of any error (raised either by MonadError or FunctorRaise).

Example:

import cats.data.EitherT
import cats.effect._
import cats.effect.syntax.bracket._
import cats.mtl._
import cats.mtl.implicits._

final case class AppError(reason: String)

class Service[F[_]: Sync: ApplicativeHandle[?[_], AppError]: FunctorRaise[?[_], AppError]] {

  def createActive: F[String] =
    create.bracketCase(uuid => makeActive(uuid)) {
      case (_, ExitCase.Completed)   => Sync[F].unit // There can be an error too
      case (uuid, ExitCase.Error(e)) => destroy(uuid)
      case (uuid, ExitCase.Canceled) => destroy(uuid)
    }

  def makeActive(uuid: String): F[String] = FunctorRaise[F, AppError].raise(AppError("Oops. Something went wrong"))
  def create: F[String]                   = Sync[F].pure("some uuid")
  def destroy(uuid: String): F[Unit]      = Sync[F].unit

}

object Service {

  def main(args: Array[String]): Unit = {
    type Effect[A] = EitherT[IO, AppError, A]
    val service = new Service[Effect]

    println(service.createActive.value.unsafeRunSync()) // Left(AppError("Oops. Something went wrong"))
  }

}

For sure this is the correct behavior of a Bracket typeclass and clean-up logic for a specific error can be managed inside the bracket:

create.bracketCase(uuid => makeActive(uuid).handleError[AppError](e => destroy(uuid) >> e.raise)) { ... }

But should there be an alternative Bracket that manages specific errors raised by FunctorRaise? For example, extended ADT can be used:

sealed trait ExitCase[+E1, +E2]
object ExitCase {
  final case object Completed                  extends ExitCase[Nothing, Nothing]
  final case class Error[E](cause: E)          extends ExitCase[E, Nothing]
  final case class UnhandledError[E](cause: E) extends ExitCase[Nothing, E]
  final case object Canceled                   extends ExitCase[Nothing, Nothing]
}
@neko-kai
Copy link
Contributor

neko-kai commented Aug 7, 2019

I'd recommend you to look into Bifunctor IO implementations such as ZIO or cats-bio – they'll behave as expected in this scenario, unlike EitherT

@iRevive
Copy link
Contributor Author

iRevive commented Aug 14, 2019

Hi @Kaishh, thanks. Bifunctor solved my problem.

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

No branches or pull requests

2 participants