package effect
- Source
- package.scala
- Alphabetic
- By Inheritance
- effect
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Package Members
Type Members
- type ApplicativeThrow[F[_]] = ApplicativeError[F, Throwable]
- trait Async[F[_]] extends Sync[F] with LiftIO[F]
A monad that can describe asynchronous or synchronous computations that produce exactly one result.
A monad that can describe asynchronous or synchronous computations that produce exactly one result.
On Asynchrony
An asynchronous task represents logic that executes independent of the main program flow, or current callstack. It can be a task whose result gets computed on another thread, or on some other machine on the network.
In terms of types, normally asynchronous processes are represented as:
(A => Unit) => Unit
This signature can be recognized in the "Observer pattern" described in the "Gang of Four", although it should be noted that without an
onComplete
event (like in the Rx Observable pattern) you can't detect completion in case this callback can be called zero or multiple times.Some abstractions allow for signaling an error condition (e.g.
MonadError
data types), so this would be a signature that's closer to Scala'sFuture#onComplete
:(Either[Throwable, A] => Unit) => Unit
And many times the abstractions built to deal with asynchronous tasks also provide a way to cancel such processes, to be used in race conditions in order to cleanup resources early:
(A => Unit) => Cancelable
This is approximately the signature of JavaScript's
setTimeout
, which will return a "task ID" that can be used to cancel it.N.B. this type class in particular is NOT describing cancelable async processes, see the Concurrent type class for that.
Async Type class
This type class allows the modeling of data types that:
- can start asynchronous processes
- can emit one result on completion
- can end in error
N.B. on the "one result" signaling, this is not an exactly once requirement. At this point streaming types can implement
Async
and such an exactly once requirement is only clear in Effect.Therefore the signature exposed by the async builder is this:
(Either[Throwable, A] => Unit) => Unit
N.B. such asynchronous processes are not cancelable. See the Concurrent alternative for that.
- Annotations
- @implicitNotFound()
- final class Blocker extends AnyVal
An execution context that is safe to use for blocking operations.
An execution context that is safe to use for blocking operations.
Used in conjunction with ContextShift, this type allows us to write functions that require a special
ExecutionContext
for evaluation, while discouraging the use of a shared, general purpose pool (e.g. the global context).Instances of this class should *NOT* be passed implicitly because they hold state and in some cases your application may need different instances of Blocker.
- trait Bracket[F[_], E] extends MonadError[F, E]
An extension of
MonadError
exposing thebracket
operation, a generalized abstracted pattern of safe resource acquisition and release in the face of errors or interruption. - type BracketThrow[F[_]] = Bracket[F, Throwable]
- type CancelToken[F[_]] = F[Unit]
A cancelation token is an effectful action that is able to cancel a running task.
A cancelation token is an effectful action that is able to cancel a running task.
This is just an alias in order to clarify the API. For example seeing
CancelToken[IO]
instead ofIO[Unit]
can be more readable.Cancelation tokens usually have these properties:
- they suspend over side effectful actions on shared state
- they need to be idempotent
Note that in the case of well behaved implementations like that of IO idempotency is taken care of by its internals whenever dealing with cancellation tokens, but idempotency is a useful property to keep in mind when building such values.
- trait Clock[F[_]] extends AnyRef
Clock provides the current time, as a pure alternative to:
Clock provides the current time, as a pure alternative to:
- Java's System.currentTimeMillis for getting the "real-time clock" and System.nanoTime for a monotonic clock useful for time measurements
- JavaScript's
Date.now()
andperformance.now()
Clock
works with anF
monadic context that can suspend side effects (e.g. IO).This is NOT a type class, as it does not have the coherence requirement.
- Annotations
- @implicitNotFound()
- trait Concurrent[F[_]] extends Async[F]
Type class for Async data types that are cancelable and can be started concurrently.
Type class for Async data types that are cancelable and can be started concurrently.
Thus this type class allows abstracting over data types that:
- implement the Async algebra, with all its restrictions
- can provide logic for cancellation, to be used in race conditions in order to release resources early (in its cancelable builder)
Due to these restrictions, this type class also affords to describe a start operation that can start async processes, suspended in the context of
F[_]
and that can be canceled or joined.Without cancellation being baked in, we couldn't afford to do it. See below.
Cancelable builder
The signature exposed by the cancelable builder is this:
(Either[Throwable, A] => Unit) => CancelToken[F]
CancelToken[F] is just an alias for
F[Unit]
and used to represent a cancellation action which will send a signal to the producer, that may observe it and cancel the asynchronous process.On Cancellation
Simple asynchronous processes, like Scala's
Future
, can be described with this very basic and side-effectful type and you should recognize what is more or less the signature ofFuture#onComplete
or of Async.async (minus the error handling):(A => Unit) => Unit
But many times the abstractions built to deal with asynchronous tasks can also provide a way to cancel such processes, to be used in race conditions in order to cleanup resources early, so a very basic and side-effectful definition of asynchronous processes that can be canceled would be:
(A => Unit) => CancelToken
This is approximately the signature of JavaScript's
setTimeout
, which will return a "task ID" that can be used to cancel it. Or of Java'sScheduledExecutorService#schedule
, which will return a JavaScheduledFuture
that has a.cancel()
operation on it.Similarly, for
Concurrent
data types, we can provide cancellation logic that can be triggered in race conditions to cancel the on-going processing, only thatConcurrent
's cancelation token is an action suspended in anF[Unit]
.Suppose you want to describe a "sleep" operation, like that described by Timer to mirror Java's
ScheduledExecutorService.schedule
or JavaScript'ssetTimeout
:def sleep(d: FiniteDuration): F[Unit]
This signature is in fact incomplete for data types that are not cancelable, because such equivalent operations always return some cancellation token that can be used to trigger a forceful interruption of the timer. This is not a normal "dispose" or "finally" clause in a try/catch block, because "cancel" in the context of an asynchronous process is concurrent with the task's own run-loop.
To understand what this means, consider that in the case of our
sleep
as described above, on cancellation we'd need a way to signal to the underlyingScheduledExecutorService
to forcefully remove the scheduledRunnable
from its internal queue of scheduled tasks, before its execution. Therefore, without a cancelable data type, a safe signature needs to return a cancellation token, so it would look like this:def sleep(d: FiniteDuration): F[(F[Unit], F[Unit])]
This function is returning a tuple, with one
F[Unit]
to wait for the completion of our sleep and a secondF[Unit]
to cancel the scheduled computation in case we need it. This is in fact the shape of Fiber's API. And this is exactly what the start operation returns.The difference between a Concurrent data type and one that is only Async is that you can go from any
F[A]
to aF[Fiber[F, A]]
, to participate in race conditions and that can be canceled should the need arise, in order to trigger an early release of allocated resources.Thus a Concurrent data type can safely participate in race conditions, whereas a data type that is only Async cannot do it without exposing and forcing the user to work with cancellation tokens. An Async data type cannot expose for example a
start
operation that is safe.Resource-safety
Concurrent data types are required to cooperate with Bracket.
Concurrent
being cancelable by law, what this means for the correspondingBracket
is that cancelation can be observed and that in the case of bracketCase the ExitCase.Canceled branch will get executed on cancelation.By default the
cancelable
builder is derived frombracketCase
and from asyncF, so what this means is that whatever you can express withcancelable
, you can also express withbracketCase
.For uncancelable, the cancel signal has no effect on the result of join and the cancelable token returned by ConcurrentEffect.runCancelable on evaluation will have no effect if evaluated.
So
uncancelable
must undo the cancellation mechanism of cancelable, with this equivalence:F.uncancelable(F.cancelable { cb => f(cb); token }) <-> F.async(f)
Sample:
val F = Concurrent[IO] val timer = Timer[IO] // Normally Timer#sleep yields cancelable tasks val tick = F.uncancelable(timer.sleep(10.seconds)) // This prints "Tick!" after 10 seconds, even if we are // canceling the Fiber after start: for { fiber <- F.start(tick) _ <- fiber.cancel _ <- fiber.join _ <- F.delay { println("Tick!") } } yield ()
When doing bracket or bracketCase,
acquire
andrelease
operations are guaranteed to be uncancelable as well.- Annotations
- @implicitNotFound()
- trait ConcurrentEffect[F[_]] extends Concurrent[F] with Effect[F]
Type class describing effect data types that are cancelable.
Type class describing effect data types that are cancelable.
In addition to the algebras of Concurrent and of Effect, instances must also implement a 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.
- Annotations
- @implicitNotFound()
- trait ContextShift[F[_]] extends AnyRef
ContextShift provides support for shifting execution.
ContextShift provides support for shifting execution.
The
shift
method inserts an asynchronous boundary, which moves execution from the calling thread to the default execution environment ofF
.The
evalOn
method provides a way to evaluate a task on a specific execution context, shifting back to the default execution context after the task completes.This is NOT a type class, as it does not have the coherence requirement.
- Annotations
- @implicitNotFound()
- trait Effect[F[_]] extends Async[F]
A monad that can suspend side effects into the
F
context and that supports lazy and potentially asynchronous evaluation.A monad that can suspend side effects into the
F
context and that supports lazy and potentially asynchronous evaluation.This type class is describing data types that:
- implement the Async algebra
- implement a lawful runAsync operation that triggers the evaluation (in the context of IO)
Note this is the safe and generic version of IO.unsafeRunAsync (aka Haskell's
unsafePerformIO
).- Annotations
- @implicitNotFound()
- sealed abstract class ExitCase[+E] extends Product with Serializable
Type for signaling the exit condition of an effectful computation, that may either succeed, fail with an error or get canceled.
Type for signaling the exit condition of an effectful computation, that may either succeed, fail with an error or get canceled.
The types of exit signals are:
- sealed abstract case class ExitCode extends Product with Serializable
Represents the exit code of an application.
Represents the exit code of an application.
code
is constrained to a range from 0 to 255, inclusive. - trait Fiber[F[_], A] extends AnyRef
Fiber
represents the (pure) result of a Concurrent data type (e.g.Fiber
represents the (pure) result of a Concurrent data type (e.g. IO) being started concurrently and that can be either joined or canceled.You can think of fibers as being lightweight threads, a fiber being a concurrency primitive for doing cooperative multi-tasking.
For example a
Fiber
value is the result of evaluating IO.start:val io = IO.shift *> IO(println("Hello!")) val fiber: IO[Fiber[IO, Unit]] = io.start
Usage example:
for { fiber <- IO.shift *> launchMissiles.start _ <- runToBunker.handleErrorWith { error => // Retreat failed, cancel launch (maybe we should // have retreated to our bunker before the launch?) fiber.cancel *> IO.raiseError(error) } aftermath <- fiber.join } yield { aftermath }
- sealed abstract class IO[+A] extends IOBinaryCompat[A]
A pure abstraction representing the intention to perform a side effect, where the result of that side effect may be obtained synchronously (via return) or asynchronously (via callback).
A pure abstraction representing the intention to perform a side effect, where the result of that side effect may be obtained synchronously (via return) or asynchronously (via callback).
IO
values are pure, immutable values and thus preserve referential transparency, being usable in functional programming. AnIO
is a data structure that represents just a description of a side effectful computation.IO
can describe synchronous or asynchronous computations that:- on evaluation yield exactly one result
- can end in either success or failure and in case of failure
flatMap
chains get short-circuited (IO
implementing the algebra ofMonadError
) - can be canceled, but note this capability relies on the user to provide cancellation logic
Effects described via this abstraction are not evaluated until the "end of the world", which is to say, when one of the "unsafe" methods are used. Effectful results are not memoized, meaning that memory overhead is minimal (and no leaks), and also that a single effect may be run multiple times in a referentially-transparent manner. For example:
val ioa = IO { println("hey!") } val program = for { _ <- ioa _ <- ioa } yield () program.unsafeRunSync()
The above will print "hey!" twice, as the effect will be re-run each time it is sequenced in the monadic chain.
IO
is trampolined in itsflatMap
evaluation. This means that you can safely callflatMap
in a recursive function of arbitrary depth, without fear of blowing the stack.def fib(n: Int, a: Long = 0, b: Long = 1): IO[Long] = IO(a + b).flatMap { b2 => if (n > 0) fib(n - 1, b, b2) else IO.pure(a) }
- trait IOApp extends AnyRef
App
type that runs a cats.effect.IO.App
type that runs a cats.effect.IO. Shutdown occurs after theIO
completes, as follows:- If completed with
ExitCode.Success
, the main method exits and shutdown is handled by the platform.- If completed with any other ExitCode,
sys.exit
is called with the specified code.- If the
IO
raises an error, the stack trace is printed to standard error andsys.exit(1)
is called.When a shutdown is requested via a signal, the
IO
is canceled and we wait for theIO
to release any resources. The process exits with the numeric value of the signal plus 128.import cats.effect._ import cats.syntax.all._ object MyApp extends IOApp { def run(args: List[String]): IO[ExitCode] = args.headOption match { case Some(name) => IO(println(s"Hello, ${name}.")).as(ExitCode.Success) case None => IO(System.err.println("Usage: MyApp name")).as(ExitCode(2)) } }
- trait LiftIO[F[_]] extends Serializable
- Annotations
- @implicitNotFound()
- trait LowPriorityImplicits extends LowerPriorityImplicits
- Attributes
- protected
- trait LowerPriorityImplicits extends AnyRef
- Attributes
- protected
- type MonadThrow[F[_]] = MonadError[F, Throwable]
- sealed abstract class Resource[+F[_], +A] extends ResourceLike[F, A]
The
Resource
is a data structure that captures the effectful allocation of a resource, along with its finalizer.The
Resource
is a data structure that captures the effectful allocation of a resource, along with its finalizer.This can be used to wrap expensive resources. Example:
def open(file: File): Resource[IO, BufferedReader] = Resource(IO { val in = new BufferedReader(new FileReader(file)) (in, IO(in.close())) })
Usage is done via use and note that resource usage nests, because its implementation is specified in terms of Bracket:
open(file1).use { in1 => open(file2).use { in2 => readFiles(in1, in2) } }
Resource
forms aMonadError
on the resource type when the effect type has acats.MonadError
instance. Nested resources are released in reverse order of acquisition. Outer resources are released even if an inner use or release fails.def mkResource(s: String) = { val acquire = IO(println(s"Acquiring $$s")) *> IO.pure(s) def release(s: String) = IO(println(s"Releasing $$s")) Resource.make(acquire)(release) } val r = for { outer <- mkResource("outer") inner <- mkResource("inner") } yield (outer, inner) r.use { case (a, b) => IO(println(s"Using $$a and $$b")) }
On evaluation the above prints:
Acquiring outer Acquiring inner Using outer and inner Releasing inner Releasing outer
A
Resource
is nothing more than a data structure, an ADT, described by the following node types and that can be interpreted if needed:Normally users don't need to care about these node types, unless conversions from
Resource
into something else is needed (e.g. conversion fromResource
into a streaming data type).- F
the effect type in which the resource is allocated and released
- A
the type of resource
- trait Sync[F[_]] extends BracketThrow[F] with Defer[F]
A monad that can suspend the execution of side effects in the
F[_]
context.A monad that can suspend the execution of side effects in the
F[_]
context.- Annotations
- @implicitNotFound()
- trait SyncEffect[F[_]] extends Sync[F]
A monad that can suspend side effects into the
F
context and that supports only synchronous lazy evaluation of these effects.A monad that can suspend side effects into the
F
context and that supports only synchronous lazy evaluation of these effects.- Annotations
- @implicitNotFound()
- final class SyncIO[+A] extends AnyRef
A pure abstraction representing the intention to perform a side effect, where the result of that side effect is obtained synchronously.
A pure abstraction representing the intention to perform a side effect, where the result of that side effect is obtained synchronously.
SyncIO
is similar to IO, but does not support asynchronous computations. Consequently, aSyncIO
can be run synchronously to obtain a result viaunsafeRunSync
. This is unlikeIO#unsafeRunSync
, which cannot be safely called in general -- doing so on the JVM blocks the calling thread while the async part of the computation is run and doing so on Scala.js throws an exception upon encountering an async boundary. - trait Timer[F[_]] extends AnyRef
Timer is a scheduler of tasks.
Timer is a scheduler of tasks.
This is the purely functional equivalent of:
- Java's ScheduledExecutorService
- JavaScript's setTimeout.
It provides:
- the ability to get the current time
- ability to delay the execution of a task with a specified time duration
It does all of that in an
F
monadic context that can suspend side effects and is capable of asynchronous execution (e.g. IO).This is NOT a type class, as it does not have the coherence requirement.
- Annotations
- @implicitNotFound()
Value Members
- object Async extends Serializable
- object Blocker extends BlockerPlatform
- object Bracket extends Serializable
- object Clock extends LowPriorityImplicits with ClockPlatform
- object Concurrent extends Serializable
- object ConcurrentEffect extends Serializable
- object ContextShift
- object Effect extends Serializable
- object ExitCase extends Serializable
- object ExitCode extends Serializable
- object Fiber extends FiberInstances
- object IO extends IOInstances
- object IOApp extends IOAppCompanionPlatform
- object LiftIO extends Serializable
- object Resource extends ResourceInstances with ResourcePlatform
- object Sync extends Serializable
- object SyncEffect extends Serializable
- object SyncIO extends SyncIOInstances
- object Timer
This is the API documentation for the cats-effect library.
See the cats.effect package for a quick overview.
Links
Canonical documentation links:
Related Cats links (the core):