Skip to content

Commit

Permalink
Merge pull request #236 from durban/topic/issue216kleisli
Browse files Browse the repository at this point in the history
Bracket instance for Kleisli
  • Loading branch information
alexandru authored May 21, 2018
2 parents 919945b + 9763d48 commit d74d5fb
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 29 deletions.
5 changes: 4 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ val mimaSettings = Seq(
exclude[DirectMissingMethodProblem]("cats.effect.internals.IOFrame.errorHandler"),
// New stuff
exclude[ReversedMissingMethodProblem]("cats.effect.internals.IOConnection.tryReactivate"),
exclude[DirectMissingMethodProblem]("cats.effect.internals.IOCancel#RaiseCancelable.this")
exclude[DirectMissingMethodProblem]("cats.effect.internals.IOCancel#RaiseCancelable.this"),
exclude[IncompatibleTemplateDefProblem]("cats.effect.Concurrent$KleisliConcurrent"),
exclude[IncompatibleTemplateDefProblem]("cats.effect.Sync$KleisliSync"),
exclude[IncompatibleTemplateDefProblem]("cats.effect.Async$KleisliAsync")
)
})

Expand Down
5 changes: 3 additions & 2 deletions core/shared/src/main/scala/cats/effect/Async.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,8 +270,9 @@ object Async {
WriterT.liftF(F.async(k))(L, FA)
}

private[effect] trait KleisliAsync[F[_], R] extends Async[Kleisli[F, R, ?]]
with Sync.KleisliSync[F, R] {
private[effect] abstract class KleisliAsync[F[_], R]
extends Sync.KleisliSync[F, R]
with Async[Kleisli[F, R, ?]] {

override protected implicit def F: Async[F]

Expand Down
46 changes: 46 additions & 0 deletions core/shared/src/main/scala/cats/effect/Bracket.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package cats
package effect

import cats.data.Kleisli

/**
* An extension of `MonadError` exposing the `bracket` operation,
* a generalized abstracted pattern of safe resource acquisition and
Expand Down Expand Up @@ -140,5 +142,49 @@ object ExitCase {
}

object Bracket {

def apply[F[_], E](implicit ev: Bracket[F, E]): Bracket[F, E] = ev

/**
* [[Bracket]] instance built for `cats.data.Kleisli` values initialized
* with any `F` data type that also implements `Bracket`.
*/
implicit def catsKleisliBracket[F[_], R, E](implicit ev: Bracket[F, E]): Bracket[Kleisli[F, R, ?], E] =
new KleisliBracket[F, R, E] { def F = ev }

private[effect] abstract class KleisliBracket[F[_], R, E] extends Bracket[Kleisli[F, R, ?], E] {

protected implicit def F: Bracket[F, E]

// NB: preferably we'd inherit things from `cats.data.KleisliApplicativeError`,
// but we can't, because it's `private[data]`, so we have to delegate.
private[this] final val kleisliMonadError: MonadError[Kleisli[F, R, ?], E] =
Kleisli.catsDataMonadErrorForKleisli

def pure[A](x: A): Kleisli[F, R, A] =
kleisliMonadError.pure(x)

def handleErrorWith[A](fa: Kleisli[F, R, A])(f: E => Kleisli[F, R, A]): Kleisli[F, R, A] =
kleisliMonadError.handleErrorWith(fa)(f)

def raiseError[A](e: E): Kleisli[F, R, A] =
kleisliMonadError.raiseError(e)

def flatMap[A, B](fa: Kleisli[F, R, A])(f: A => Kleisli[F, R, B]): Kleisli[F, R, B] =
kleisliMonadError.flatMap(fa)(f)

def tailRecM[A, B](a: A)(f: A => Kleisli[F, R, Either[A, B]]): Kleisli[F, R, B] =
kleisliMonadError.tailRecM(a)(f)

def bracketCase[A, B](acquire: Kleisli[F, R, A])
(use: A => Kleisli[F, R, B])
(release: (A, ExitCase[E]) => Kleisli[F, R, Unit]): Kleisli[F, R, B] = {

Kleisli { r =>
F.bracketCase(acquire.run(r))(a => use(a).run(r)) { (a, br) =>
release(a, br).run(r)
}
}
}
}
}
7 changes: 4 additions & 3 deletions core/shared/src/main/scala/cats/effect/Concurrent.scala
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ object Concurrent {
F.pure(Some(Right((fiberT[A](fiberA), r))))
}
})

def uncancelable[A](fa: OptionT[F, A]): OptionT[F, A] =
OptionT(F.uncancelable(fa.value))

Expand Down Expand Up @@ -634,8 +634,9 @@ object Concurrent {
Fiber(WriterT(fiber.join), WriterT.liftF(fiber.cancel))
}

private[effect] trait KleisliConcurrent[F[_], R] extends Concurrent[Kleisli[F, R, ?]]
with Async.KleisliAsync[F, R] {
private[effect] abstract class KleisliConcurrent[F[_], R]
extends Async.KleisliAsync[F, R]
with Concurrent[Kleisli[F, R, ?]] {

override protected implicit def F: Concurrent[F]
// Needed to drive static checks, otherwise the
Expand Down
29 changes: 6 additions & 23 deletions core/shared/src/main/scala/cats/effect/Sync.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,36 +228,19 @@ object Sync {
WriterT(F.suspend(thunk.run))
}

private[effect] trait KleisliSync[F[_], R] extends Sync[Kleisli[F, R, ?]] {
protected implicit def F: Sync[F]
private[effect] abstract class KleisliSync[F[_], R]
extends Bracket.KleisliBracket[F, R, Throwable]
with Sync[Kleisli[F, R, ?]] {

def pure[A](x: A): Kleisli[F, R, A] =
Kleisli.pure(x)
protected implicit override def F: Sync[F]

def handleErrorWith[A](fa: Kleisli[F, R, A])(f: Throwable => Kleisli[F, R, A]): Kleisli[F, R, A] =
override def handleErrorWith[A](fa: Kleisli[F, R, A])(f: Throwable => Kleisli[F, R, A]): Kleisli[F, R, A] =
Kleisli { r => F.suspend(F.handleErrorWith(fa.run(r))(e => f(e).run(r))) }

def raiseError[A](e: Throwable): Kleisli[F, R, A] =
Kleisli.liftF(F.raiseError(e))

def flatMap[A, B](fa: Kleisli[F, R, A])(f: A => Kleisli[F, R, B]): Kleisli[F, R, B] =
override def flatMap[A, B](fa: Kleisli[F, R, A])(f: A => Kleisli[F, R, B]): Kleisli[F, R, B] =
Kleisli { r => F.suspend(fa.run(r).flatMap(f.andThen(_.run(r)))) }

def tailRecM[A, B](a: A)(f: A => Kleisli[F, R, Either[A, B]]): Kleisli[F, R, B] =
Kleisli.catsDataMonadForKleisli[F, R].tailRecM(a)(f)

def suspend[A](thunk: => Kleisli[F, R, A]): Kleisli[F, R, A] =
Kleisli(r => F.suspend(thunk.run(r)))

def bracketCase[A, B](acquire: Kleisli[F, R, A])
(use: A => Kleisli[F, R, B])
(release: (A, ExitCase[Throwable]) => Kleisli[F, R, Unit]): Kleisli[F, R, B] = {

Kleisli { r =>
F.bracketCase(acquire.run(r))(a => use(a).run(r)) { (a, br) =>
release(a, br).run(r)
}
}
}
}
}
2 changes: 2 additions & 0 deletions laws/shared/src/test/scala/cats/effect/InstancesTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ class InstancesTests extends BaseTestsSuite {

checkAllAsync("Kleisli[IO, ?]",
implicit ec => ConcurrentTests[Kleisli[IO, Int, ?]].concurrent[Int, Int, Int])
checkAllAsync("Kleisli[IO, ?]",
implicit ec => BracketTests[Kleisli[IO, Int, ?], Throwable].bracket[Int, Int, Int])

checkAllAsync("EitherT[IO, Throwable, ?]",
implicit ec => ConcurrentEffectTests[EitherT[IO, Throwable, ?]].concurrentEffect[Int, Int, Int])
Expand Down

0 comments on commit d74d5fb

Please sign in to comment.