IO

sealed abstract class IO[+A] extends IOPlatform[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).

IO values are pure, immutable values and thus preserve referential transparency, being usable in functional programming. An IO is a data structure that represents just a description of a side effectful computation.

IO can describe synchronous or asynchronous computations that:

  1. on evaluation yield exactly one result
  2. can end in either success or failure and in case of failure flatMap chains get short-circuited (IO implementing the algebra of MonadError)
  3. can be canceled, but note this capability relies on the user to provide cancelation 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 its flatMap evaluation. This means that you can safely call flatMap 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.pure(a + b) flatMap { b2 =>
   if (n > 0)
     fib(n - 1, b, b2)
   else
     IO.pure(a)
 }
See also

IOApp for the preferred way of executing whole programs wrapped in IO

Companion
object
class IOPlatform[A]
class Object
trait Matchable
class Any

Value members

Concrete methods

def !>[B](that: IO[B]): IO[B]
def &>[B](that: IO[B]): IO[B]

Runs this IO and the parameter in parallel.

Runs this IO and the parameter in parallel.

Failure in either of the IOs will cancel the other one. If the whole computation is canceled, both actions are also canceled.

def *>[B](that: IO[B]): IO[B]

Runs the current IO, then runs the parameter, keeping its result. The result of the first action is ignored. If the source fails, the other action won't run. Not suitable for use when the parameter is a recursive reference to the current expression.

Runs the current IO, then runs the parameter, keeping its result. The result of the first action is ignored. If the source fails, the other action won't run. Not suitable for use when the parameter is a recursive reference to the current expression.

See also

&gt;&gt; for the recursion-safe, lazily evaluated alternative

def <&[B](that: IO[B]): IO[A]

Like &amp;&gt;, but keeps the result of the source

Like &amp;&gt;, but keeps the result of the source

def <*[B](that: IO[B]): IO[A]

Like *&gt;, but keeps the result of the source.

Like *&gt;, but keeps the result of the source.

For a similar method that also runs the parameter in case of failure or interruption, see guarantee.

def >>[B](that: => IO[B]): IO[B]

Runs the current IO, then runs the parameter, keeping its result. The result of the first action is ignored. If the source fails, the other action won't run. Evaluation of the parameter is done lazily, making this suitable for recursion.

Runs the current IO, then runs the parameter, keeping its result. The result of the first action is ignored. If the source fails, the other action won't run. Evaluation of the parameter is done lazily, making this suitable for recursion.

See also

[*>] for the strictly evaluated alternative

def as[B](b: B): IO[B]

Replaces the result of this IO with the given value.

Replaces the result of this IO with the given value.

def attempt: IO[Either[Throwable, A]]

Materializes any sequenced exceptions into value space, where they may be handled.

Materializes any sequenced exceptions into value space, where they may be handled.

This is analogous to the catch clause in try/catch, being the inverse of IO.raiseError. Thus:

IO.raiseError(ex).attempt.unsafeRunAsync === Left(ex)
See also

Returns a resource that will start execution of this IO in the background.

Returns a resource that will start execution of this IO in the background.

In case the resource is closed while this IO is still running (e.g. due to a failure in use), the background action will be canceled.

See also

cats.effect.kernel.GenSpawn#background for the generic version.

def backgroundOn(ec: ExecutionContext): ResourceIO[IO[OutcomeIO[A]]]
def both[B](that: IO[B]): IO[(A, B)]

Runs the current and given IO in parallel, producing the pair of the results. If either fails with an error, the result of the whole will be that error and the other will be canceled.

Runs the current and given IO in parallel, producing the pair of the results. If either fails with an error, the result of the whole will be that error and the other will be canceled.

See also

bothOutcome for the version which produces the outcome of both effects executed in parallel

race for the version which produces the result of the winner and cancels the loser of the race

def bothOutcome[B](that: IO[B]): IO[(OutcomeIO[A], OutcomeIO[B])]

Runs the current and given IO in parallel, producing the pair of the outcomes. Both outcomes are produced, regardless of whether they complete successfully.

Runs the current and given IO in parallel, producing the pair of the outcomes. Both outcomes are produced, regardless of whether they complete successfully.

See also

both for the version which embeds the outcomes to produce a pair of the results

raceOutcome for the version which produces the outcome of the winner and cancels the loser of the race

def bracket[B](use: A => IO[B])(release: A => IO[Unit]): IO[B]

Returns an IO action that treats the source task as the acquisition of a resource, which is then exploited by the use function and then released.

Returns an IO action that treats the source task as the acquisition of a resource, which is then exploited by the use function and then released.

The bracket operation is the equivalent of the try {} catch {} finally {} statements from mainstream languages.

The bracket operation installs the necessary exception handler to release the resource in the event of an exception being raised during the computation, or in case of cancelation.

If an exception is raised, then bracket will re-raise the exception ''after'' performing the release. If the resulting task gets canceled, then bracket will still perform the release, but the yielded task will be non-terminating (equivalent with IO.never).

Example:

 import java.io._

 def readFile(file: File): IO[String] = {
   // Opening a file handle for reading text
   val acquire = IO(new BufferedReader(
     new InputStreamReader(new FileInputStream(file), "utf-8")
   ))

   acquire.bracket { in =>
     // Usage part
     IO {
       // Yes, ugly Java, non-FP loop;
       // side-effects are suspended though
       var line: String = null
       val buff = new StringBuilder()
       do {
         line = in.readLine()
         if (line != null) buff.append(line)
       } while (line != null)
       buff.toString()
     }
   } { in =>
     // The release part
     IO(in.close())
   }
 }

Note that in case of cancelation the underlying implementation cannot guarantee that the computation described by use doesn't end up executed concurrently with the computation from release. In the example above that ugly Java loop might end up reading from a BufferedReader that is already closed due to the task being canceled, thus triggering an error in the background with nowhere to get signaled.

In this particular example, given that we are just reading from a file, it doesn't matter. But in other cases it might matter, as concurrency on top of the JVM when dealing with I/O might lead to corrupted data.

For those cases you might want to do synchronization (e.g. usage of locks and semaphores) and you might want to use bracketCase, the version that allows you to differentiate between normal termination and cancelation.

'''NOTE on error handling''': in case both the release function and the use function throws, the error raised by release gets signaled.

For example:

 val foo = new RuntimeException("Foo")
 val bar = new RuntimeException("Bar")
 IO("resource").bracket { _ =>
   // use
   IO.raiseError(foo)
 } { _ =>
   // release
   IO.raiseError(bar)
 }

In this case the resulting IO will raise error foo, while the bar error gets reported on a side-channel. This is consistent with the behavior of Java's "Try with resources" except that no involved exceptions are mutated (i.e., in contrast to Java, bar isn't added as a suppressed exception to foo).

Value Params
release

is a function that gets called after use terminates, either normally or in error, or if it gets canceled, receiving as input the resource that needs to be released

use

is a function that evaluates the resource yielded by the source, yielding a result that will get generated by the task returned by this bracket function

See also
def bracketCase[B](use: A => IO[B])(release: (A, OutcomeIO[B]) => IO[Unit]): IO[B]

Returns a new IO task that treats the source task as the acquisition of a resource, which is then exploited by the use function and then released, with the possibility of distinguishing between normal termination and cancelation, such that an appropriate release of resources can be executed.

Returns a new IO task that treats the source task as the acquisition of a resource, which is then exploited by the use function and then released, with the possibility of distinguishing between normal termination and cancelation, such that an appropriate release of resources can be executed.

The bracketCase operation is the equivalent of try {} catch {} finally {} statements from mainstream languages when used for the acquisition and release of resources.

The bracketCase operation installs the necessary exception handler to release the resource in the event of an exception being raised during the computation, or in case of cancelation.

In comparison with the simpler bracket version, this one allows the caller to differentiate between normal termination, termination in error and cancelation via an Outcome parameter.

Value Params
release

is a function that gets called after use terminates, either normally or in error, or if it gets canceled, receiving as input the resource that needs release, along with the result of use (cancelation, error or successful result)

use

is a function that evaluates the resource yielded by the source, yielding a result that will get generated by this function on evaluation

See also
def delayBy(duration: FiniteDuration): IO[A]

Returns an IO that will delay the execution of the source by the given duration.

Returns an IO that will delay the execution of the source by the given duration.

def evalOn(ec: ExecutionContext): IO[A]

Shifts the execution of the current IO to the specified ExecutionContext. All stages of the execution will default to the pool in question, and any asynchronous callbacks will shift back to the pool upon completion. Any nested use of evalOn will override the specified pool. Once the execution fully completes, default control will be shifted back to the enclosing (inherited) pool.

Shifts the execution of the current IO to the specified ExecutionContext. All stages of the execution will default to the pool in question, and any asynchronous callbacks will shift back to the pool upon completion. Any nested use of evalOn will override the specified pool. Once the execution fully completes, default control will be shifted back to the enclosing (inherited) pool.

See also

IO.executionContext for obtaining the ExecutionContext on which the current IO is being executed

def flatMap[B](f: A => IO[B]): IO[B]

Monadic bind on IO, used for sequentially composing two IO actions, where the value produced by the first IO is passed as input to a function producing the second IO action.

Monadic bind on IO, used for sequentially composing two IO actions, where the value produced by the first IO is passed as input to a function producing the second IO action.

Due to this operation's signature, flatMap forces a data dependency between two IO actions, thus ensuring sequencing (e.g. one action to be executed before another one).

Any exceptions thrown within the function will be caught and sequenced into the IO, because due to the nature of asynchronous processes, without catching and handling exceptions, failures would be completely silent and IO references would never terminate on evaluation.

def flatTap[B](f: A => IO[B]): IO[A]
def flatten[B](implicit ev: A <:< IO[B]): IO[B]
def forceR[B](that: IO[B]): IO[B]
def foreverM: IO[Nothing]

Evaluates the current IO in an infinite loop, terminating only on error or cancelation.

Evaluates the current IO in an infinite loop, terminating only on error or cancelation.

 IO.println("Hello, World!").foreverM    // continues printing forever
def guarantee(finalizer: IO[Unit]): IO[A]

Executes the given finalizer when the source is finished, either in success or in error, or if canceled.

Executes the given finalizer when the source is finished, either in success or in error, or if canceled.

This variant of guaranteeCase evaluates the given finalizer regardless of how the source gets terminated:

  • normal completion
  • completion in error
  • cancelation

This equivalence always holds:

 io.guarantee(f) <-> IO.unit.bracket(_ => io)(_ => f)
See also

guaranteeCase for the version that can discriminate between termination conditions

def guaranteeCase(finalizer: OutcomeIO[A] => IO[Unit]): IO[A]

Executes the given finalizer when the source is finished, either in success or in error, or if canceled, allowing for differentiating between exit conditions.

Executes the given finalizer when the source is finished, either in success or in error, or if canceled, allowing for differentiating between exit conditions.

This variant of guarantee injects an Outcome in the provided function, allowing one to make a difference between:

  • normal completion
  • completion in error
  • cancelation

This equivalence always holds:

 io.guaranteeCase(f) <-> IO.unit.bracketCase(_ => io)((_, e) => f(e))
See also

guarantee for the simpler version

def handleError[B >: A](f: Throwable => B): IO[B]
def handleErrorWith[B >: A](f: Throwable => IO[B]): IO[B]

Handle any error, potentially recovering from it, by mapping it to another IO value.

Handle any error, potentially recovering from it, by mapping it to another IO value.

Implements ApplicativeError.handleErrorWith.

def ifM[B](ifTrue: => IO[B], ifFalse: => IO[B])(implicit ev: A <:< Boolean): IO[B]
def iterateUntil(p: A => Boolean): IO[A]
def iterateWhile(p: A => Boolean): IO[A]
def map[B](f: A => B): IO[B]

Functor map on IO. Given a mapping function, it transforms the value produced by the source, while keeping the IO context.

Functor map on IO. Given a mapping function, it transforms the value produced by the source, while keeping the IO context.

Any exceptions thrown within the function will be caught and sequenced into the IO. Due to the nature of asynchronous processes, without catching and handling exceptions, failures would be completely silent and IO references would never terminate on evaluation.

def memoize: IO[IO[A]]
def onCancel(fin: IO[Unit]): IO[A]
def onError(f: Throwable => IO[Unit]): IO[A]
def option: IO[Option[A]]

Replaces failures in this IO with an empty Option.

Replaces failures in this IO with an empty Option.

def product[B](that: IO[B]): IO[(A, B)]
def productL[B](that: IO[B]): IO[A]
def productR[B](that: IO[B]): IO[B]
def race[B](that: IO[B]): IO[Either[A, B]]
def raceOutcome[B](that: IO[B]): IO[Either[OutcomeIO[A], OutcomeIO[B]]]
def racePair[B](that: IO[B]): IO[Either[(OutcomeIO[A], FiberIO[B]), (FiberIO[A], OutcomeIO[B])]]
def redeem[B](recover: Throwable => B, map: A => B): IO[B]

Returns a new value that transforms the result of the source, given the recover or map functions, which get executed depending on whether the result ends in error or if it is successful.

Returns a new value that transforms the result of the source, given the recover or map functions, which get executed depending on whether the result ends in error or if it is successful.

This is an optimization on usage of attempt and map, this equivalence being true:

 io.redeem(recover, map) <-> io.attempt.map(_.fold(recover, map))

Usage of redeem subsumes handleError because:

 io.redeem(fe, id) <-> io.handleError(fe)
Value Params
map

is a function used for mapping the result of the source in case it ends in success

recover

is a function used for error recover in case the source ends in error

def redeemWith[B](recover: Throwable => IO[B], bind: A => IO[B]): IO[B]

Returns a new value that transforms the result of the source, given the recover or bind functions, which get executed depending on whether the result ends in error or if it is successful.

Returns a new value that transforms the result of the source, given the recover or bind functions, which get executed depending on whether the result ends in error or if it is successful.

This is an optimization on usage of attempt and flatMap, this equivalence being available:

 io.redeemWith(recover, bind) <-> io.attempt.flatMap(_.fold(recover, bind))

Usage of redeemWith subsumes handleErrorWith because:

 io.redeemWith(fe, F.pure) <-> io.handleErrorWith(fe)

Usage of redeemWith also subsumes flatMap because:

 io.redeemWith(F.raiseError, fs) <-> io.flatMap(fs)
Value Params
bind

is the function that gets to transform the source in case of success

recover

is the function that gets called to recover the source in case of error

def start: IO[FiberIO[A]]

Start execution of the source suspended in the IO context.

Start execution of the source suspended in the IO context.

This can be used for non-deterministic / concurrent execution. The following code is more or less equivalent with parMap2 (minus the behavior on error handling and cancelation):

 def par2[A, B](ioa: IO[A], iob: IO[B]): IO[(A, B)] =
   for {
     fa <- ioa.start
     fb <- iob.start
      a <- fa.join
      b <- fb.join
   } yield (a, b)

Note in such a case usage of parMapN (via cats.Parallel) is still recommended because of behavior on error and cancelation — consider in the example above what would happen if the first task finishes in error. In that case the second task doesn't get canceled, which creates a potential memory leak.

Also see background for a safer alternative.

def startOn(ec: ExecutionContext): IO[FiberIO[A]]
def syncStep: SyncIO[Either[IO[A], A]]

Translates this IO[A] into a SyncIO value which, when evaluated, runs the original IO to its completion, or until the first asynchronous, boundary, whichever is encountered first.

Translates this IO[A] into a SyncIO value which, when evaluated, runs the original IO to its completion, or until the first asynchronous, boundary, whichever is encountered first.

def timed: IO[(FiniteDuration, A)]
def timeout[A2 >: A](duration: FiniteDuration): IO[A2]

Returns an IO that either completes with the result of the source within the specified time duration or otherwise raises a TimeoutException.

Returns an IO that either completes with the result of the source within the specified time duration or otherwise raises a TimeoutException.

The source is canceled in the event that it takes longer than the specified time duration to complete.

Value Params
duration

is the time span for which we wait for the source to complete; in the event that the specified time has passed without the source completing, a TimeoutException is raised

def timeoutTo[A2 >: A](duration: FiniteDuration, fallback: IO[A2]): IO[A2]

Returns an IO that either completes with the result of the source within the specified time duration or otherwise evaluates the fallback.

Returns an IO that either completes with the result of the source within the specified time duration or otherwise evaluates the fallback.

The source is canceled in the event that it takes longer than the FiniteDuration to complete, the evaluation of the fallback happening immediately after that.

Value Params
duration

is the time span for which we wait for the source to complete; in the event that the specified time has passed without the source completing, the fallback gets evaluated

fallback

is the task evaluated after the duration has passed and the source canceled

def to[F[_]](implicit F: LiftIO[F]): F[A]

Converts the source IO into any F type that implements the LiftIO type class.

Converts the source IO into any F type that implements the LiftIO type class.

override def toString: String
Definition Classes
Any
def uncancelable: IO[A]

Makes the source IO uninterruptible such that a cats.effect.kernel.Fiber#cancel signal is ignored until completion.

Makes the source IO uninterruptible such that a cats.effect.kernel.Fiber#cancel signal is ignored until completion.

See also

IO.uncancelable for constructing uncancelable IO values with user-configurable cancelable regions

def unsafeRunAndForget(implicit runtime: IORuntime): Unit

Triggers the evaluation of the source and any suspended side effects therein, but ignores the result.

Triggers the evaluation of the source and any suspended side effects therein, but ignores the result.

This operation is similar to unsafeRunAsync, in that the evaluation can happen asynchronously, except no callback is required and therefore the result is ignored.

Note that errors still get logged (via IO's internal logger), because errors being thrown should never be totally silent.

def unsafeRunAsync(cb: Either[Throwable, A] => Unit)(implicit runtime: IORuntime): Unit

Passes the result of the encapsulated effects to the given callback by running them as impure side effects.

Passes the result of the encapsulated effects to the given callback by running them as impure side effects.

Any exceptions raised within the effect will be passed to the callback in the Either. The callback will be invoked at most once. Note that it is very possible to construct an IO which never returns while still never blocking a thread, and attempting to evaluate that IO with this method will result in a situation where the callback is never invoked.

As the name says, this is an UNSAFE function as it is impure and performs side effects. You should ideally only call this function ''once'', at the very end of your program.

def unsafeRunAsyncOutcome(cb: Outcome[Id, Throwable, A] => Unit)(implicit runtime: IORuntime): Unit
def unsafeRunCancelable(implicit runtime: IORuntime): () => Future[Unit]

Evaluates the effect, returning a cancelation token that can be used to cancel it.

Evaluates the effect, returning a cancelation token that can be used to cancel it.

This is similar to unsafeRunAsync in that it evaluates the IO as a side effect in a non-blocking fashion, but uses a Future rather than an explicit callback. This function should really only be used if interoperating with code which uses Scala futures.

See also
def unsafeToFuture(implicit runtime: IORuntime): Future[A]

Evaluates the effect and produces the result in a Future.

Evaluates the effect and produces the result in a Future.

This is similar to unsafeRunAsync in that it evaluates the IO as a side effect in a non-blocking fashion, but uses a Future rather than an explicit callback. This function should really only be used if interoperating with code which uses Scala futures.

See also
def unsafeToFutureCancelable(implicit runtime: IORuntime): (Future[A], () => Future[Unit])

Evaluates the effect and produces the result in a Future, along with a cancelation token that can be used to cancel the original effect.

Evaluates the effect and produces the result in a Future, along with a cancelation token that can be used to cancel the original effect.

This is similar to unsafeRunAsync in that it evaluates the IO as a side effect in a non-blocking fashion, but uses a Future rather than an explicit callback. This function should really only be used if interoperating with code which uses Scala futures.

See also
def untilM[G[_], B >: A](cond: => IO[Boolean])(implicit evidence$2: Alternative[G]): IO[G[B]]
def untilM_(cond: => IO[Boolean]): IO[Unit]
def void: IO[Unit]

Ignores the result of this IO.

Ignores the result of this IO.

def whileM[G[_], B >: A](p: IO[Boolean])(implicit evidence$1: Alternative[G]): IO[G[B]]
def whileM_(p: IO[Boolean]): IO[Unit]

Inherited methods

def unsafeToPromise(implicit runtime: IORuntime): Promise[A]
Inherited from
IOPlatform