/
ConcurrentEffect.scala
136 lines (119 loc) · 4.99 KB
/
ConcurrentEffect.scala
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
* Copyright (c) 2017-2018 The Typelevel Cats-effect Project Developers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cats
package effect
import simulacrum._
import cats.data.{EitherT, StateT, WriterT}
import scala.annotation.implicitNotFound
import scala.util.Either
/**
* Type class describing effect data types that are cancelable.
*
* In addition to the algebras of [[Concurrent]] and of
* [[Effect]], instances must also implement a
* [[ConcurrentEffect!.runCancelable runCancelable]] operation that
* triggers the evaluation, suspended in the `IO` context, but that
* also returns a token that can be used for canceling the running
* computation.
*
* Note this is the safe and generic version of [[IO.unsafeRunCancelable]].
*/
@typeclass
@implicitNotFound("""Cannot find implicit value for ConcurrentEffect[${F}].
Building this implicit value might depend on having an implicit
s.c.ExecutionContext in scope, a Timer, Scheduler or some equivalent type.""")
trait ConcurrentEffect[F[_]] extends Concurrent[F] with Effect[F] {
/**
* Evaluates `F[_]` with the ability to cancel it.
*
* The returned `IO[IO[Unit]]` is a suspended cancelable action that
* can be used to cancel the running computation.
*
* Note that evaluating the returned `IO` value, along with
* the boxed cancelable action are guaranteed to have immediate
* (synchronous) execution so you can safely do this, even
* on top of JavaScript (which has no ability to block threads):
*
* {{{
* val io = F.runCancelable(fa)(cb)
*
* // For triggering asynchronous execution
* val cancel = io.unsafeRunSync
* // For cancellation
* cancel.unsafeRunSync
* }}}
*/
def runCancelable[A](fa: F[A])(cb: Either[Throwable, A] => IO[Unit]): IO[IO[Unit]]
override def toIO[A](fa: F[A]): IO[A] =
ConcurrentEffect.toIOFromRunCancelable(fa)(this)
}
object ConcurrentEffect {
/**
* [[ConcurrentEffect.toIO]] default implementation, derived from
* [[ConcurrentEffect.runCancelable]].
*/
def toIOFromRunCancelable[F[_], A](fa: F[A])(implicit F: ConcurrentEffect[F]): IO[A] =
IO.cancelable { cb =>
F.runCancelable(fa)(r => IO(cb(r))).unsafeRunSync()
}
/**
* [[ConcurrentEffect]] instance built for `cats.data.EitherT` values initialized
* with any `F` data type that also implements `ConcurrentEffect`.
*/
implicit def catsEitherTConcurrentEffect[F[_]: ConcurrentEffect]: ConcurrentEffect[EitherT[F, Throwable, ?]] =
new EitherTConcurrentEffect[F] { def F = ConcurrentEffect[F] }
/**
* [[ConcurrentEffect]] instance built for `cats.data.StateT` values initialized
* with any `F` data type that also implements `ConcurrentEffect`.
*/
implicit def catsStateTConcurrentEffect[F[_]: ConcurrentEffect, S: Monoid]: ConcurrentEffect[StateT[F, S, ?]] =
new StateTConcurrentEffect[F, S] { def F = ConcurrentEffect[F]; def S = Monoid[S] }
/**
* [[ConcurrentEffect]] instance built for `cats.data.WriterT` values initialized
* with any `F` data type that also implements `ConcurrentEffect`.
*/
implicit def catsWriterTConcurrentEffect[F[_]: ConcurrentEffect, L: Monoid]: ConcurrentEffect[WriterT[F, L, ?]] =
new WriterTConcurrentEffect[F, L] { def F = ConcurrentEffect[F]; def L = Monoid[L] }
private[effect] trait EitherTConcurrentEffect[F[_]]
extends ConcurrentEffect[EitherT[F, Throwable, ?]]
with Concurrent.EitherTConcurrent[F, Throwable]
with Effect.EitherTEffect[F] {
protected def F: ConcurrentEffect[F]
def runCancelable[A](fa: EitherT[F, Throwable, A])
(cb: Either[Throwable, A] => IO[Unit]): IO[IO[Unit]] =
F.runCancelable(fa.value)(cb.compose(_.right.flatMap(x => x)))
}
private[effect] trait StateTConcurrentEffect[F[_], S]
extends ConcurrentEffect[StateT[F, S, ?]]
with Concurrent.StateTConcurrent[F, S]
with Effect.StateTEffect[F, S] {
protected def F: ConcurrentEffect[F]
protected def S: Monoid[S]
def runCancelable[A](fa: StateT[F, S, A])
(cb: Either[Throwable, A] => IO[Unit]): IO[IO[Unit]] =
F.runCancelable(fa.runA(S.empty)(F))(cb)
}
private[effect] trait WriterTConcurrentEffect[F[_], L]
extends ConcurrentEffect[WriterT[F, L, ?]]
with Concurrent.WriterTConcurrent[F, L]
with Effect.WriterTEffect[F, L] {
protected def F: ConcurrentEffect[F]
protected def L: Monoid[L]
def runCancelable[A](fa: WriterT[F, L, A])
(cb: Either[Throwable, A] => IO[Unit]): IO[IO[Unit]] =
F.runCancelable(fa.run)(cb.compose(_.right.map(_._2)))
}
}