-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathManagedT.scala
More file actions
105 lines (91 loc) · 3.48 KB
/
Copy pathManagedT.scala
File metadata and controls
105 lines (91 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.iravid.managedt
import cats.kernel.{ Monoid, Semigroup }
import cats.{ Applicative, Monad, MonadError, StackSafeMonad }
import cats.implicits._
abstract class ManagedT[F[_], R] {
/**
* Use the resource in this ManagedT in the provided function. Cleanup handlers will
* be sequenced after the F[A] returned from use.
*/
def apply[A](use: R => F[A]): F[A]
/**
* Extract the resource in this ManagedT. The cleanup handlers will have already run
* before actions sequenced after the returned F[A].
*
* Using this doesn't make much sense if the cleanup handlers invalidate the resource,
* so use with caution.
*/
def unwrap(implicit F: Applicative[F]): F[R] = apply(F.pure)
}
object ManagedT extends ManagedTLowPriority {
/**
* Construct a new ManagedT that acquires a resource using `acquire` and releases it
* with `cleanup` after it is used.
*/
def apply[F[_], R](acquire: => F[R])(cleanup: R => F[Unit])(
implicit FE: MonadError[F, Throwable]): ManagedT[F, R] =
new ManagedT[F, R] {
def apply[A](use: R => F[A]): F[A] =
for {
resource <- acquire
resultOrError <- use(resource).attempt
result <- resultOrError match {
case Left(e) => cleanup(resource) *> e.raiseError[F, A]
case Right(a) => cleanup(resource) as a
}
} yield result
}
/**
* Lifts a value in `F` into `ManagedT[F, A]` *with no cleanup handler*. Use with
* caution!
*/
def liftF[F[_], A](fa: => F[A])(implicit FE: MonadError[F, Throwable]): ManagedT[F, A] =
ManagedT(fa)(_ => FE.unit)
/**
* Lifts a value into `ManagedT[F, A]` *with no cleanup handler*. Use with
* caution!
*/
def pure[F[_]: MonadError[?[_], Throwable]] = new PartiallyAppliedBuilder[F]
class PartiallyAppliedBuilder[F[_]](implicit FE: MonadError[F, Throwable]) {
def apply[A](a: => A): ManagedT[F, A] = liftF(FE.pure(a))
}
implicit def monad[F[_]](
implicit
FE: MonadError[F, Throwable]): Monad[ManagedT[F, ?]] =
new Monad[ManagedT[F, ?]] with StackSafeMonad[ManagedT[F, ?]] {
def pure[A](x: A): ManagedT[F, A] =
apply(x.pure[F])(_ => FE.unit)
def flatMap[R1, R2](fa: ManagedT[F, R1])(f: R1 => ManagedT[F, R2]): ManagedT[F, R2] =
new ManagedT[F, R2] {
def apply[A](use: R2 => F[A]): F[A] =
fa { r1 =>
f(r1) { r2 =>
use(r2)
}
}
}
override def tailRecM[R1, R2](r1: R1)(f: R1 => ManagedT[F, Either[R1, R2]]): ManagedT[F, R2] =
new ManagedT[F, R2] {
def apply[A](use: R2 => F[A]): F[A] =
f(r1) {
case Left(r1) => tailRecM(r1)(f)(use)
case Right(r2) => use(r2)
}
}
}
implicit def monoid[F[_], A](implicit A: Monoid[A],
FE: MonadError[F, Throwable]): Monoid[ManagedT[F, A]] =
new Monoid[ManagedT[F, A]] {
def combine(x: ManagedT[F, A], y: ManagedT[F, A]): ManagedT[F, A] =
monad[F].map2(x, y)(A.combine)
def empty: ManagedT[F, A] = monad[F].pure(A.empty)
}
}
trait ManagedTLowPriority {
implicit def semigroup[F[_], A](implicit FE: MonadError[F, Throwable],
A: Semigroup[A]): Semigroup[ManagedT[F, A]] =
new Semigroup[ManagedT[F, A]] {
def combine(x: ManagedT[F, A], y: ManagedT[F, A]): ManagedT[F, A] =
ManagedT.monad[F].map2(x, y)(A.combine)
}
}