Options
All
  • Public
  • Public/Protected
  • All
Menu

IO represents a specification for a possibly lazy or asynchronous computation, which when executed will produce an A as a result, along with possible side-effects.

Compared with Funfix's Future (see funfix-exec) or JavaScript's Promise, IO does not represent a running computation or a value detached from time, as IO does not execute anything when working with its builders or operators and it does not submit any work into the Scheduler or any run-loop for execution, the execution eventually taking place only after IO.run is called and not before that.

In order to understand IO, here's the design space:

Strict Lazy
Synchronous A () => A
Eval<A>
Asynchronous (Try<A> => void) => void () => ((Try<A> => void) => void)
Future<A> / Promise IO<A>

JavaScript is a language (and runtime) that's strict by default, meaning that expressions are evaluated immediately instead of being evaluated on a by-need basis, like in Haskell.

So a value A is said to be strict. To turn an A value into a lazy value, you turn that expression into a parameterless function of type () => A, also called a "thunk".

A Future is a value that's produced by an asynchronous process, but it is said to have strict behavior, meaning that when you receive a Future reference, whatever process that's supposed to complete the Future has probably started already. This goes for JavaScript's Promise as well.

But there are cases where we don't want strict values, but lazily evaluated ones. In some cases we want functions, or Future-generators. Because we might want better handling of parallelism, or we might want to suspend side effects. As without suspending side effects we don't have referential transparency, which really helps with reasoning about the code, being the essence of functional programming.

This IO type is thus the complement to Future, a lazy, lawful monadic type that can describe any side effectful action, including asynchronous ones, also capable of suspending side effects.

Getting Started

To build an IO from a parameterless function returning a value (a thunk), we can use IO.of:

const hello = IO.of(() => "Hello ")
const world = IO.of(() => "World!")

Nothing gets executed yet, as IO is lazy, nothing executes until you trigger run on it.

To combine IO values we can use map and flatMap, which describe sequencing and this time is in a very real sense because of the laziness involved:

const sayHello = hello
  .flatMap(h => world.map(w => h + w))
  .map(console.info)

This IO reference will trigger a side effect on evaluation, but not yet. To make the above print its message:

const f: Future<void> = sayHello.run()

//=> Hello World!

The returned type is a Future, a value that can be completed already or might be completed at some point in the future, once the running asynchronous process finishes. It's the equivalent of JavaScript's Promise, only better and cancelable, see next topic.

Laziness

The fact that IO is lazy, whereas Future and Promise are not has real consequences. For example with IO you can do this:

function retryOnFailure<A>(times: number, io: IO<A>): IO<A> {
  return source.recoverWith(err => {
    // No more retries left? Re-throw error:
    if (times <= 0) return IO.raise(err)
    // Recursive call, yes we can!
    return retryOnFailure(times - 1, io)
      // Adding 500 ms delay for good measure
      .delayExecution(500)
  })
}

Future being a strict value-wannabe means that the actual value gets "memoized" (means cached), however IO is basically a function that can be repeated for as many times as you want. IO can also do memoization of course:

io.memoize()

The difference between this and just calling run() is that memoize() still returns an IO and the actual memoization happens on the first run() (with idempotency guarantees of course).

But here's something else that Future or your favorite Promise-like data type cannot do:

io.memoizeOnSuccess()

This keeps repeating the computation for as long as the result is a failure and caches it only on success. Yes we can!

Parallelism

Because of laziness, invoking IO.sequence will not work like it does for Future.sequence or Promise.all, the given IO values being evaluated one after another, in sequence, not in parallel. If you want parallelism, then you need to use IO.gather and thus be explicit about it.

This is great because it gives you the possibility of fine tuning the execution. For example, say you want to execute things in parallel, but with a maximum limit of 30 tasks being executed in parallel. One way of doing that is to process your list in batches.

This sample assumes you have lodash installed, for manipulating our array:

import * as _ from "lodash"
import { IO } from "funfix"

// Some array of IOs, you come up with something good :-)
const list: IO<string>[] = ???

// Split our list in chunks of 30 items per chunk,
// this being the maximum parallelism allowed
const chunks = _.chunks(list, 30)
// Specify that each batch should process stuff in parallel
const batchedIOs = _.map(chunks, chunk => IO.gather(chunk))
// Sequence the batches
const allBatches = IO.sequence(batchedIOs)

// Flatten the result, within the context of IO
const all: IO<string[]> =
  allBatches.map(batches => _.flatten(batches))

Note that the built IO reference is just a specification at this point, or you can view it as a function, as nothing has executed yet, you need to call .run explicitly.

Cancellation

The logic described by an IO task could be cancelable, depending on how the IO gets built. This is where the IO-Future symbiosis comes into play.

Futures can also be canceled, in case the described computation can be canceled. When describing IO tasks with IO.of nothing can be cancelled, since there's nothing about a plain function that you can cancel, but, we can build cancelable tasks with IO.async:

import { Cancelable, Success, IO } from "funfix"

const delayedHello = IO.async((scheduler, callback) => {
  const task = scheduler.scheduleOnce(1000, () => {
    console.info("Delayed Hello!")
    // Signaling successful completion
    // ("undefined" inhabits type "void")
    callback(Success(undefined))
  })

  return Cancelable.of(() => {
    console.info("Cancelling!")
    task.cancel()
  })
})

The sample above prints a message with a delay, where the delay itself is scheduled with the injected Scheduler. The Scheduler is in fact an optional parameter to IO.run and if one isn't explicitly provided, then Scheduler.global is assumed.

This action can be cancelled, because it specifies cancellation logic. If we wouldn't return an explicit Cancelable there, then cancellation wouldn't work. But for this IO reference it does:

// Triggering execution, which sends a task to execute by means
// of JavaScript's setTimeout (under the hood):
const f: Future<void> = delayedHello.run()

// If we change our mind before the timespan has passed:
f.cancel()

Also, given an IO task, we can specify actions that need to be triggered in case of cancellation:

const io = IO.of(() => console.info("Hello!"))
  .executeForked()

io.doOnCancel(IO.of(() => {
  console.info("A cancellation attempt was made!")
})

const f: Future<void> = io.run()

// Note that in this case cancelling the resulting Future
// will not stop the actual execution, since it doesn't know
// how, but it will trigger our on-cancel callback:

f.cancel()
//=> A cancellation attempt was made!

Note on the ExecutionModel

IO is conservative in how it introduces async boundaries. Transformations like map and flatMap for example will default to being executed on the current call stack on which the asynchronous computation was started. But one shouldn't make assumptions about how things will end up executed, as ultimately it is the implementation's job to decide on the best execution model. All you are guaranteed is asynchronous execution after executing run.

Currently the default ExecutionModel specifies batched execution by default and IO in its evaluation respects the injected ExecutionModel. If you want a different behavior, you need to execute the IO reference with a different scheduler.

In order to configure a different execution model, this config can be injected by means of a custom scheduler:

import { Scheduler, ExecutionModel } from "funfix"

const ec = Scheduler.global.get()
  .withExecutionModel(ExecutionModel.alwaysAsync())

// ...
io.run(ec)

Or you can configure an IO reference to execute with a certain execution model that overrides the configuration of the injected scheduler, by means of IO.executeWithModel:

io.executeWithModel(ExecutionModel.batched(256))

Versus Eval

For dealing with lazy evaluation, the other alternative is the Eval data type.

Differences between Eval and IO:

  1. IO is capable of describing asynchronous computations as well
  2. IO is capable of error handling (it implements MonadError), whereas Eval does not provide error handling capabilities, being meant to be used for pure expressions (it implements Comonad, which is incompatible with MonadError)
  3. You cannot rely on IO to produce a value immediately, since we cannot block threads on top of JavaScript engines

So if you need error handling capabilities (i.e. MonadError<Throwable, ?>), or if you need to describe asynchronous processes, then IO is for you. Eval is a simpler data type with the sole purpose of controlling the evaluation of expressions (i.e. strict versus lazy).

Credits

This type is inspired by cats.effect.IO from Typelevel Cats, by monix.eval.Task from Monix, by scalaz.effect.IO from Scalaz, which are all inspired by Haskell's IO data type.

final

Type parameters

  • A

Hierarchy

  • IO

Index

Methods

asyncBoundary

  • asyncBoundary(ec?: Scheduler): IO<A>
  • Introduces an asynchronous boundary at the current stage in the asynchronous processing pipeline (after the source has been evaluated).

    Consider the following example:

    const readPath: () => "path/to/file"
    
    const io = IO.of(readPath)
      .asyncBoundary()
      .map(fs.readFileSync)
    

    Between reading the path and then reading the file from that path, we schedule an async boundary (it usually happens with JavaScript's setTimeout under the hood).

    This is equivalent with:

    self.flatMap(a => IO.shift(ec).map(_ => a))
    
    // ... or ...
    
    self.forEffect(IO.shift(ec))
    

    Also see IO.shift and IO.fork.

    Parameters

    • Optional ec: Scheduler

      is an optional Scheduler implementation that can be used for scheduling the async boundary, however if not specified, the IO's default scheduler (the one passed to run()) gets used

    Returns IO<A>

attempt

  • attempt(): IO<Either<Throwable, A>>
  • Handle errors by lifting results into Either values.

    If there's an error, then a Left value will be signaled. If there is no error, then a Right value will be signaled instead.

    The returned type is an Either value, which is what's called a "logical disjunction" or a "tagged union type", representing a choice between two values, in this case errors on the "Left" and successful results on the "Right".

    // Describing an IO that can fail on execution:
    const io: IO<number> = IO.of(() => {
      const n = Math.random() * 1000
      const m = n & n // to integer
      if (m % 2) throw new Error("No odds please!")
      return m
    })
    
    // By using attempt() we can observe and use errors
    // in `map` and `flatMap` transformations:
    io.attempt().map(either =>
      either.fold(
        err => "odd",
        val => "even"
      ))
    

    For other error handling capabilities, see IO.recoverWith and IO.transformWith.

    Returns IO<Either<Throwable, A>>

chain

  • chain<B>(f: function): IO<B>
  • Alias for .flatMap.

    Type parameters

    • B

    Parameters

    • f: function
        • (a: A): IO<B>
        • Parameters

          • a: A

          Returns IO<B>

    Returns IO<B>

delayExecution

  • delayExecution(delay: number | Duration): IO<A>
  • Delays the evaluation of this IO by the specified duration.

    const fa = IO.of(() => "Hello")
    
    // Delays the evaluation by 1 second
    fa.delayExecution(1000)
    

    Parameters

    • delay: number | Duration

      is the duration to wait before signaling the final result

    Returns IO<A>

delayResult

  • delayResult(delay: number | Duration): IO<A>
  • Delays signaling the result of this IO on evaluation by the specified duration.

    It works for successful results:

    const fa = IO.of(() => "Alex")
    
    // Delays the signaling by 1 second
    fa.delayResult(1000)
    

    And for failures as well:

    Future.raise(new TimeoutError()).delayResult(1000)
    

    Parameters

    • delay: number | Duration

      is the duration to wait before signaling the final result

    Returns IO<A>

doOnCancel

  • doOnCancel(callback: IO<void>): IO<A>
  • Returns a new IO that will mirror the source, but that will execute the given callback if the task gets canceled before completion.

    This only works for premature cancellation. See IO.doOnFinish for triggering callbacks when the source finishes.

    Parameters

    • callback: IO<void>

      is the IO value to execute if the task gets canceled prematurely

    Returns IO<A>

doOnFinish

  • doOnFinish(f: function): IO<A>
  • Returns a new IO in which f is scheduled to be run on completion. This would typically be used to release any resources acquired by this IO.

    The returned IO completes when both the source and the task returned by f complete.

    NOTE: The given function is only called when the task is complete. However the function does not get called if the task gets canceled. Cancellation is a process that's concurrent with the execution of a task and hence needs special handling.

    See IO.doOnCancel for specifying a callback to call on canceling a task.

    Parameters

    • f: function
        • (e: Option<Throwable>): IO<void>
        • Parameters

          • e: Option<Throwable>

          Returns IO<void>

    Returns IO<A>

executeForked

  • executeForked(ec?: Scheduler): IO<A>
  • Ensures that an asynchronous boundary happens before the execution, managed by the provided scheduler.

    Alias for IO.fork.

    Calling this is equivalent with:

    IO.shift(ec).flatMap(_ => self)
    
    // ... or ...
    
    IO.shift(ec).followedBy(self)
    

    See IO.fork, IO.asyncBoundary and IO.shift.

    Parameters

    • Optional ec: Scheduler

    Returns IO<A>

executeWithModel

  • executeWithModel(em: ExecutionModel): IO<A>
  • Override the ExecutionModel of the default scheduler.

    import { ExecutionModel } from "funfix"
    
    io.executeWithModel(ExecutionModel.alwaysAsync())
    

    Parameters

    • em: ExecutionModel

    Returns IO<A>

executeWithOptions

  • Returns a new IO that upon evaluation will execute with the given set of IOOptions, allowing for tuning the run-loop.

    This allows for example making run-loops "auto-cancelable", an option that's off by default due to safety concerns:

    io.executeWithOptions({
      autoCancelableRunLoops: true
    })
    

    Parameters

    Returns IO<A>

flatMap

  • flatMap<B>(f: function): IO<B>
  • Creates a new IO by applying a function to the successful result of the source, and returns a new instance equivalent to the result of the function.

    const rndInt = IO.of(() => {
      const nr = Math.random() * 1000000
      return nr & nr
    })
    
    const evenInt = () =>
      rndInt.flatMap(int => {
        if (i % 2 == 0)
          return IO.now(i)
        else // Retry until we have an even number!
          return evenInt()
      })
    

    Type parameters

    • B

    Parameters

    • f: function
        • (a: A): IO<B>
        • Parameters

          • a: A

          Returns IO<B>

    Returns IO<B>

followedBy

  • followedBy<B>(fb: IO<B>): IO<B>
  • Sequentially compose two IO actions, discarding any value produced by the first.

    So this:

    ioA.followedBy(ioB)
    

    Is equivalent with this:

    ioA.flatMap(_ => fb)
    

    Type parameters

    • B

    Parameters

    • fb: IO<B>

    Returns IO<B>

forEach

  • forEach(cb: function): IO<void>
  • Returns a new IO that upon evaluation will execute the given function for the generated element, transforming the source into an IO<void>.

    Parameters

    • cb: function
        • (a: A): void
        • Parameters

          • a: A

          Returns void

    Returns IO<void>

forEffect

  • forEffect<B>(fb: IO<B>): IO<A>
  • Sequentially compose two actions, discarding any value produced by the second.

    So this:

    ioA.forEffect(ioB)
    

    Is equivalent with this:

    ioA.flatMap(a => ioB.map(_ => a))
    

    Type parameters

    • B

    Parameters

    • fb: IO<B>

    Returns IO<A>

map

  • map<B>(f: function): IO<B>
  • Returns a new IO that applies the mapping function to the successful result emitted by the source.

    IO.now(111).map(_ => _ * 2).get() // 222
    

    Note there's a correspondence between flatMap and map:

    fa.map(f) <-> fa.flatMap(x => IO.pure(f(x)))
    

    Type parameters

    • B

    Parameters

    • f: function
        • (a: A): B
        • Parameters

          • a: A

          Returns B

    Returns IO<B>

memoize

  • memoize(): IO<A>
  • Memoizes (caches) the result of the source IO and reuses it on subsequent invocations of run.

    The resulting task will be idempotent, meaning that evaluating the resulting task multiple times will have the same effect as evaluating it once.

    see

    IO.memoizeOnSuccess for a version that only caches successful results.

    Returns IO<A>

memoizeOnSuccess

  • memoizeOnSuccess(): IO<A>
  • Memoizes (caches) the successful result of the source task and reuses it on subsequent invocations of run. Thrown exceptions are not cached.

    The resulting task will be idempotent, but only if the result is successful.

    see

    IO.memoize for a version that caches both successful results and failures

    Returns IO<A>

recover

  • recover<AA>(f: function): IO<A | AA>
  • Creates a new IO that will mirror the source on success, but on failure it will try to recover and yield a successful result by applying the given function f to the thrown error.

    This function is the equivalent of a try/catch statement, or the equivalent of .map for errors.

    io.recover(err => {
      console.error(err)
      fallback
    })
    

    Type parameters

    • AA

    Parameters

    • f: function
        • (e: Throwable): AA
        • Parameters

          • e: Throwable

          Returns AA

    Returns IO<A | AA>

recoverWith

  • recoverWith<AA>(f: function): IO<A | AA>
  • Creates a new IO that will mirror the source on success, but on failure it will try to recover and yield a successful result by applying the given function f to the thrown error.

    This function is the equivalent of a try/catch statement, or the equivalent of .flatMap for errors.

    Note that because of IO's laziness, this can describe retry loop:

    function retryOnFailure<A>(times: number, io: IO<A>): IO<A> {
      return source.recoverWith(err => {
        // No more retries left? Re-throw error:
        if (times <= 0) return IO.raise(err)
        // Recursive call, yes we can!
        return retryOnFailure(times - 1, io)
          // Adding 500 ms delay for good measure
          .delayExecution(500)
      })
    }
    

    Type parameters

    • AA

    Parameters

    • f: function
        • (e: Throwable): IO<AA>
        • Parameters

          • e: Throwable

          Returns IO<AA>

    Returns IO<A | AA>

run

  • run(ec?: Scheduler): Future<A>
  • Triggers the asynchronous execution.

    Without invoking run on a IO, nothing gets evaluated, as an IO has lazy behavior.

    // Describing a side effect
    const io = IO.of(() => console.log("Hello!"))
      // Delaying it for 1 second, for didactical purposes
      .delayExecution(1000)
    
    // Nothing executes until we call run on it, which gives
    // us a Future in return:
    const f: Future<void> = io.run()
    
    // The given Future is cancelable, in case the logic
    // decribed by our IO is cancelable, so we can do this:
    f.cancel()
    

    Note that run takes a Scheduler as an optional parameter and if one isn't provided, then the default scheduler gets used. The Scheduler is in charge of scheduling asynchronous boundaries, executing tasks with a delay (e.g. setTimeout) or of reporting failures (with console.error by default).

    Also see IO.runOnComplete for a version that takes a callback as parameter.

    Parameters

    • Default value ec: Scheduler = Scheduler.global.get()

    Returns Future<A>

    a Future that will eventually complete with the result produced by this IO on evaluation

runOnComplete

  • runOnComplete(cb: function, ec?: Scheduler): ICancelable
  • Triggers the asynchronous execution.

    Without invoking run on a IO, nothing gets evaluated, as an IO has lazy behavior.

    runComplete starts the evaluation and takes a callback which will be triggered when the computation is complete.

    Compared with JavaScript's Promise.then the provided callback is a function that receives a Try value, a data type which is what's called a "logical disjunction", or a "tagged union type", a data type that can represent both successful results and failures. This is because in Funfix we don't work with null.

    Also the returned value is an ICancelable reference, which can be used to cancel the running computation, in case the logic described by our IO is cancelable (note that some procedures cannot be cancelled, it all depends on how the IO value was described, see IO.async for how cancelable IO values can be built).

    Example:

    // Describing a side effect
    const io = IO.of(() => console.log("Hello!"))
      .delayExecution(1000)
    
    // Nothing executes until we explicitly run our `IO`:
    const c: ICancelable = io.runOnComplete(r =>
      r.fold(
        err => console.error(err),
        _ => console.info("Done!")
      ))
    
    // In case we change our mind and the logic described by
    // our `IO` is cancelable, we can cancel it:
    c.cancel()
    

    Note that runOnComplete takes a Scheduler as an optional parameter and if one isn't provided, then the default scheduler gets used. The Scheduler is in charge of scheduling asynchronous boundaries, executing tasks with a delay (e.g. setTimeout) or of reporting failures (with console.error by default).

    Also see IO.run for a version that returns a Future, which might be easier to work with, especially since a Future is Promise-like.

    Parameters

    • cb: function

      is the callback that will be eventually called with the final result, or error, when the evaluation completes

        • (result: Try<A>): void
        • Parameters

          • result: Try<A>

          Returns void

    • Default value ec: Scheduler = Scheduler.global.get()

      is the scheduler that controls the triggering of asynchronous boundaries (e.g. setTimeout)

    Returns ICancelable

    a cancelable action that can be triggered to cancel the running computation, assuming that the implementation of the source IO can be cancelled

timeout

  • timeout(after: number | Duration): IO<A>
  • Returns an IO that mirrors the source in case the result of the source is signaled within the required after duration on evaluation, otherwise it fails with a TimeoutError, cancelling the source.

    const fa = IO.of(() => 1).delayResult(10000)
    
    // Will fail with a TimeoutError on run()
    fa.timeout(1000)
    

    Parameters

    • after: number | Duration

      is the duration to wait until it triggers the timeout error

    Returns IO<A>

timeoutTo

  • timeoutTo<AA>(after: number | Duration, fallback: IO<AA>): IO<A | AA>
  • Returns an IO value that mirrors the source in case the result of the source is signaled within the required after duration when evaluated (with run()), otherwise it triggers the execution of the given fallback after the duration has passed, cancelling the source.

    This is literally the implementation of IO.timeout:

    const fa = IO.of(() => 1).delayResult(10000)
    
    fa.timeoutTo(1000, IO.raise(new TimeoutError()))
    

    Type parameters

    • AA

    Parameters

    • after: number | Duration

      is the duration to wait until it triggers the fallback

    • fallback: IO<AA>

      is a fallback IO to timeout to

    Returns IO<A | AA>

transform

  • transform<R>(failure: function, success: function): IO<R>
  • Creates a new IO by applying the 'success' function to the successful result of the source, or the 'error' function to the potential errors that might happen.

    This function is similar with .map, except that it can also transform errors and not just successful results.

    Type parameters

    • R

    Parameters

    • failure: function

      is a function for transforming failures

        • (e: Throwable): R
        • Parameters

          • e: Throwable

          Returns R

    • success: function

      is a function for transforming a successful result

        • (a: A): R
        • Parameters

          • a: A

          Returns R

    Returns IO<R>

transformWith

  • transformWith<R>(failure: function, success: function): IO<R>
  • Creates a new IO by applying the 'success' function to the successful result of the source, or the 'error' function to the potential errors that might happen.

    This function is similar with .flatMap, except that it can also transform errors and not just successful results.

    Type parameters

    • R

    Parameters

    • failure: function

      is a function for transforming failures

        • (e: Throwable): IO<R>
        • Parameters

          • e: Throwable

          Returns IO<R>

    • success: function

      is a function for transforming a successful result

        • (a: A): IO<R>
        • Parameters

          • a: A

          Returns IO<R>

    Returns IO<R>

Static always

  • always<A>(thunk: function): IO<A>
  • Promote a thunk function to an IO, catching exceptions in the process.

    Note that since IO is not memoized by global, this will recompute the value each time the IO is executed.

    const io = IO.always(() => { console.log("Hello!") })
    
    io.run()
    //=> Hello!
    io.run()
    //=> Hello!
    io.run()
    //=> Hello!
    

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): A
        • Returns A

    Returns IO<A>

Static async

  • async<A>(register: function): IO<A>
  • Create a IO from an asynchronous computation, which takes the form of a function with which we can register a callback.

    This can be used to translate from a callback-based API to a straightforward monadic version.

    Type parameters

    • A

    Parameters

    • register: function
        • (ec: Scheduler, cb: function): ICancelable | void
        • Parameters

          • ec: Scheduler
          • cb: function
              • (a: Try<A>): void
              • Parameters

                • a: Try<A>

                Returns void

          Returns ICancelable | void

    Returns IO<A>

Static asyncUnsafe

  • Constructs a lazy IO instance whose result will be computed asynchronously.

    WARNING: Unsafe to use directly, only use if you know what you're doing. For building IO instances safely see IO.async.

    Rules of usage:

    • the received StackedCancelable can be used to store cancelable references that will be executed upon cancel; every push must happen at the beginning, before any execution happens and pop must happen afterwards when the processing is finished, before signaling the result
    • before execution, an asynchronous boundary is recommended, to avoid stack overflow errors, but can happen using the scheduler's facilities for trampolined execution
    • on signaling the result (Success or Failure), another async boundary is necessary, but can also happen with the scheduler's facilities for trampolined execution (e.g. Scheduler.trampoline)

    WARNING: note that not only is this builder unsafe, but also unstable, as the IORegister callback type is exposing volatile internal implementation details. This builder is meant to create optimized asynchronous tasks, but for normal usage prefer IO.async.

    Type parameters

    • A

    Parameters

    Returns IO<A>

Static defer

  • defer<A>(thunk: function): IO<A>
  • Promote a thunk function generating IO results to an IO of the same type.

    Alias for IO.suspend.

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): IO<A>
        • Returns IO<A>

    Returns IO<A>

Static deferAction

  • deferAction<A>(f: function): IO<A>
  • Defers the creation of an IO by using the provided function, which has the ability to inject a needed Scheduler.

    Example:

    function measureLatency<A>(source: IO<A>): IO<[A, Long]> {
      return IO.deferAction<[A, Long]>(s => {
        // We have our Scheduler, which can inject time, we
        // can use it for side-effectful operations
        const start = s.currentTimeMillis()
    
        return source.map(a => {
          const finish = s.currentTimeMillis()
          return [a, finish - start]
        })
      })
    }
    

    Type parameters

    • A

    Parameters

    • f: function

      is the function that's going to be called when the resulting IO gets evaluated

        • (ec: Scheduler): IO<A>
        • Parameters

          • ec: Scheduler

          Returns IO<A>

    Returns IO<A>

Static deferFuture

  • deferFuture<A>(thunk: function): IO<A>
  • Given a thunk that produces Future values, suspends it in the IO context, evaluating it on demand whenever the resulting IO gets evaluated.

    See IO.fromFuture for the strict version.

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): Future<A>
        • Returns Future<A>

    Returns IO<A>

Static deferFutureAction

  • deferFutureAction<A>(f: function): IO<A>
  • Wraps calls that generate Future results into IO, provided a callback with an injected Scheduler.

    This builder helps with wrapping Future-enabled APIs that need a Scheduler to work.

    Type parameters

    • A

    Parameters

    • f: function

      is the function that's going to be executed when the task gets evaluated, generating the wrapped Future

        • (ec: Scheduler): Future<A>
        • Parameters

          • ec: Scheduler

          Returns Future<A>

    Returns IO<A>

Static delayedTick

  • delayedTick<A>(delay: number | Duration): IO<void>
  • Returns an IO that on evaluation will complete after the given delay.

    This can be used to do delayed execution. For example:

    IO.delayedTick(1000).flatMap(_ =>
      IO.of(() => console.info("Hello!"))
    )
    

    Type parameters

    • A

    Parameters

    • delay: number | Duration

      is the duration to wait before signaling the tick

    Returns IO<void>

Static firstCompletedOf

  • firstCompletedOf<A>(list: IO<A>[] | Iterable<IO<A>>): IO<A>
  • Creates a race condition between multiple IO values, on evaluation returning the result of the first one that completes, cancelling the rest.

    const failure = IO.raise(new TimeoutError()).delayResult(2000)
    
    // Will yield 1
    const fa1 = IO.of(() => 1).delayResult(1000)
    IO.firstCompletedOf([fa1, failure])
    
    // Will yield a TimeoutError
    const fa2 = IO.of(() => 1).delayResult(10000)
    IO.firstCompletedOf([fa2, failure])
    

    Type parameters

    • A

    Parameters

    • list: IO<A>[] | Iterable<IO<A>>

      is the list of IO values for which the race is started

    Returns IO<A>

    a new IO that will evaluate to the result of the first in the list to complete, the rest being cancelled

Static fork

  • fork<A>(fa: IO<A>, ec?: Scheduler): IO<A>
  • Mirrors the given source IO, but before execution trigger an asynchronous boundary (usually by means of setTimeout on top of JavaScript, depending on the provided Scheduler implementation).

    If a Scheduler is not explicitly provided, the implementation ends up using the one provided in IO.run.

    Note that IO.executeForked is the method version of this function (e.g. io.executeForked() == IO.fork(this)).

    IO.of(() => fs.readFileSync(path))
      .executeForked()
    

    Also see IO.shift and IO.asyncBoundary.

    Type parameters

    • A

    Parameters

    • fa: IO<A>

      is the task that will get executed asynchronously

    • Optional ec: Scheduler

      is the Scheduler used for triggering the async boundary, or if not provided it will default to the scheduler passed on evaluation in IO.run

    Returns IO<A>

Static fromFuture

  • fromFuture<A>(fa: Future<A>): IO<A>
  • Converts any strict Future value into an IO.

    Note that this builder does not suspend any side effects, since the given parameter is strict (and not a function) and because Future has strict behavior.

    See IO.deferFuture for an alternative that evaluates lazy thunks that produce future results.

    Type parameters

    • A

    Parameters

    • fa: Future<A>

    Returns IO<A>

Static fromTry

  • fromTry<A>(a: Try<A>): IO<A>
  • Returns a IO reference that will signal the result of the given Try<A> reference upon evaluation.

    Type parameters

    • A

    Parameters

    • a: Try<A>

    Returns IO<A>

Static gather

  • gather<A>(list: IO<A>[] | Iterable<IO<A>>): IO<A[]>
  • Nondeterministically gather results from the given collection of tasks, returning a task that will signal the same type of collection of results once all tasks are finished.

    This function is the nondeterministic analogue of sequence and should behave identically to sequence so long as there is no interaction between the effects being gathered. However, unlike sequence, which decides on a total order of effects, the effects in a gather are unordered with respect to each other.

    In other words gather can execute IO tasks in parallel, whereas IO.sequence forces an execution order.

    Although the effects are unordered, the order of results matches the order of the input sequence.

    const io1 = IO.of(() => 1)
    const io2 = IO.of(() => 2)
    const io3 = IO.of(() => 3)
    
    // Yields [1, 2, 3]
    const all: IO<number[]> = IO.gather([f1, f2, f3])
    

    Type parameters

    • A

    Parameters

    • list: IO<A>[] | Iterable<IO<A>>

    Returns IO<A[]>

Static map2

  • map2<A1, A2, R>(fa1: IO<A1>, fa2: IO<A2>, f: function): IO<R>
  • Maps 2 IO values by the mapping function, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.sequence operation and as such on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    
    
    // Yields Success(3)
    IO.map2(fa1, fa2, (a, b) => a + b)
    
    // Yields Failure, because the second arg is a Failure
    IO.map2(fa1, IO.raise("error"),
      (a, b) => a + b
    )
    

    This operation is the Applicative.map2.

    Type parameters

    • A1

    • A2

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • f: function
        • (a1: A1, a2: A2): R
        • Parameters

          • a1: A1
          • a2: A2

          Returns R

    Returns IO<R>

Static map3

  • map3<A1, A2, A3, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, f: function): IO<R>
  • Maps 3 IO values by the mapping function, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.sequence operation and as such on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    
    
    // Yields Success(6)
    IO.map3(fa1, fa2, fa3, (a, b, c) => a + b + c)
    
    // Yields Failure, because the second arg is a Failure
    IO.map3(
      fa1, fa2, IO.raise("error"),
      (a, b, c) => a + b + c
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • f: function
        • (a1: A1, a2: A2, a3: A3): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3

          Returns R

    Returns IO<R>

Static map4

  • map4<A1, A2, A3, A4, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, f: function): IO<R>
  • Maps 4 IO values by the mapping function, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.sequence operation and as such on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    
    // Yields Success(10)
    IO.map4(fa1, fa2, fa3, fa4, (a, b, c, d) => a + b + c + d)
    
    // Yields Failure, because the second arg is a Failure
    IO.map4(
      fa1, fa2, fa3, IO.raise("error"),
      (a, b, c, d) => a + b + c + d
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4

          Returns R

    Returns IO<R>

Static map5

  • map5<A1, A2, A3, A4, A5, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, fa5: IO<A5>, f: function): IO<R>
  • Maps 5 IO values by the mapping function, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.sequence operation and as such on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    const fa5 = IO.of(() => 5)
    
    // Yields Success(15)
    IO.map5(fa1, fa2, fa3, fa4, fa5,
      (a, b, c, d, e) => a + b + c + d + e
    )
    
    // Yields Failure, because the second arg is a Failure
    IO.map5(
      fa1, fa2, fa3, fa4, IO.raise("error"),
      (a, b, c, d, e) => a + b + c + d + e
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • A5

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • fa5: IO<A5>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4
          • a5: A5

          Returns R

    Returns IO<R>

Static map6

  • map6<A1, A2, A3, A4, A5, A6, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, fa5: IO<A5>, fa6: IO<A6>, f: function): IO<R>
  • Maps 6 IO values by the mapping function, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.sequence operation and as such on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    const fa5 = IO.of(() => 5)
    const fa6 = IO.of(() => 6)
    
    // Yields Success(21)
    IO.map6(
      fa1, fa2, fa3, fa4, fa5, fa6,
      (a, b, c, d, e, f) => a + b + c + d + e + f
    )
    
    // Yields Failure, because the second arg is a Failure
    IO.map6(
      fa1, fa2, fa3, fa4, fa5, IO.raise("error"),
      (a, b, c, d, e, f) => a + b + c + d + e + f
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • fa5: IO<A5>
    • fa6: IO<A6>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4
          • a5: A5
          • a6: A6

          Returns R

    Returns IO<R>

Static now

  • now<A>(value: A): IO<A>
  • Returns an IO that on execution is always successful, emitting the given strict value.

    Type parameters

    • A

    Parameters

    • value: A

    Returns IO<A>

Static of

  • of<A>(thunk: function): IO<A>
  • Alias for IO.always.

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): A
        • Returns A

    Returns IO<A>

Static once

  • once<A>(thunk: function): IO<A>
  • Promote a thunk function to a Coeval that is memoized on the first evaluation, the result being then available on subsequent evaluations.

    Note this is equivalent with:

    IO.always(thunk).memoize()
    

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): A
        • Returns A

    Returns IO<A>

Static parMap2

  • parMap2<A1, A2, R>(fa1: IO<A1>, fa2: IO<A2>, f: function): IO<R>
  • Maps 2 IO values evaluated nondeterministically, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.gather operation. As such the IO operations are potentially executed in parallel (if the operations are asynchronous) and on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    
    
    // Yields Success(3)
    IO.parMap2(fa1, fa2, (a, b) => a + b)
    
    // Yields Failure, because the second arg is a Failure
    IO.parMap2(fa1, IO.raise("error"),
      (a, b) => a + b
    )
    

    Type parameters

    • A1

    • A2

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • f: function
        • (a1: A1, a2: A2): R
        • Parameters

          • a1: A1
          • a2: A2

          Returns R

    Returns IO<R>

Static parMap3

  • parMap3<A1, A2, A3, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, f: function): IO<R>
  • Maps 3 IO values evaluated nondeterministically, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.gather operation. As such the IO operations are potentially executed in parallel (if the operations are asynchronous) and on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    
    
    // Yields Success(6)
    IO.parMap3(fa1, fa2, fa3, (a, b, c) => a + b + c)
    
    // Yields Failure, because the second arg is a Failure
    IO.parMap3(
      fa1, fa2, IO.raise("error"),
      (a, b, c) => a + b + c
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • f: function
        • (a1: A1, a2: A2, a3: A3): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3

          Returns R

    Returns IO<R>

Static parMap4

  • parMap4<A1, A2, A3, A4, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, f: function): IO<R>
  • Maps 4 IO values evaluated nondeterministically, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.gather operation. As such the IO operations are potentially executed in parallel (if the operations are asynchronous) and on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    
    // Yields Success(10)
    IO.parMap4(fa1, fa2, fa3, fa4, (a, b, c, d) => a + b + c + d)
    
    // Yields Failure, because the second arg is a Failure
    IO.parMap4(
      fa1, fa2, fa3, IO.raise("error"),
      (a, b, c, d) => a + b + c + d
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4

          Returns R

    Returns IO<R>

Static parMap5

  • parMap5<A1, A2, A3, A4, A5, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, fa5: IO<A5>, f: function): IO<R>
  • Maps 5 IO values evaluated nondeterministically, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.gather operation. As such the IO operations are potentially executed in parallel (if the operations are asynchronous) and on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    const fa5 = IO.of(() => 5)
    
    // Yields Success(15)
    IO.parMap5(fa1, fa2, fa3, fa4, fa5,
      (a, b, c, d, e) => a + b + c + d + e
    )
    
    // Yields Failure, because the second arg is a Failure
    IO.parMap5(
      fa1, fa2, fa3, fa4, IO.raise("error"),
      (a, b, c, d, e) => a + b + c + d + e
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • A5

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • fa5: IO<A5>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4
          • a5: A5

          Returns R

    Returns IO<R>

Static parMap6

  • parMap6<A1, A2, A3, A4, A5, A6, R>(fa1: IO<A1>, fa2: IO<A2>, fa3: IO<A3>, fa4: IO<A4>, fa5: IO<A5>, fa6: IO<A6>, f: function): IO<R>
  • Maps 6 IO values evaluated nondeterministically, returning a new IO reference that completes with the result of mapping that function to the successful values of the futures, or in failure in case either of them fails.

    This is a specialized IO.gather operation. As such the IO operations are potentially executed in parallel (if the operations are asynchronous) and on cancellation or failure all pending tasks get cancelled.

    const fa1 = IO.of(() => 1)
    const fa2 = IO.of(() => 2)
    const fa3 = IO.of(() => 3)
    const fa4 = IO.of(() => 4)
    const fa5 = IO.of(() => 5)
    const fa6 = IO.of(() => 6)
    
    // Yields Success(21)
    IO.parMap6(
      fa1, fa2, fa3, fa4, fa5, fa6,
      (a, b, c, d, e, f) => a + b + c + d + e + f
    )
    
    // Yields Failure, because the second arg is a Failure
    IO.parMap6(
      fa1, fa2, fa3, fa4, fa5, IO.raise("error"),
      (a, b, c, d, e, f) => a + b + c + d + e + f
    )
    

    Type parameters

    • A1

    • A2

    • A3

    • A4

    • A5

    • A6

    • R

    Parameters

    • fa1: IO<A1>
    • fa2: IO<A2>
    • fa3: IO<A3>
    • fa4: IO<A4>
    • fa5: IO<A5>
    • fa6: IO<A6>
    • f: function
        • (a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6): R
        • Parameters

          • a1: A1
          • a2: A2
          • a3: A3
          • a4: A4
          • a5: A5
          • a6: A6

          Returns R

    Returns IO<R>

Static pure

  • pure<A>(value: A): IO<A>
  • Lifts a value into the IO context.

    Alias for IO.now.

    Type parameters

    • A

    Parameters

    • value: A

    Returns IO<A>

Static raise

  • raise<A>(e: Throwable): IO<A>
  • Returns an IO that on execution is always finishing in error emitting the specified exception.

    Type parameters

    • A

    Parameters

    • e: Throwable

    Returns IO<A>

Static sequence

  • sequence<A>(list: IO<A>[] | Iterable<IO<A>>): IO<A[]>
  • Transforms a list of IO values into an IO of a list, ordering both results and side effects.

    This operation would be the equivalent of Promise.all or of Future.sequence, however because of the laziness of IO the given values are processed in order.

    Sequencing means that on evaluation the tasks won't get processed in parallel. If parallelism is desired, see IO.gather.

    Sample:

    const io1 = IO.of(() => 1)
    const io2 = IO.of(() => 2)
    const io3 = IO.of(() => 3)
    
    // Yields [1, 2, 3]
    const all: IO<number[]> = IO.sequence([f1, f2, f3])
    

    Type parameters

    • A

    Parameters

    • list: IO<A>[] | Iterable<IO<A>>

    Returns IO<A[]>

Static shift

  • shift(ec?: Scheduler): IO<void>
  • Shifts the bind continuation of the IO onto the specified scheduler, for triggering asynchronous execution.

    Asynchronous actions cannot be shifted, since they are scheduled rather than run. Also, no effort is made to re-shift synchronous actions which follow asynchronous actions within a bind chain; those actions will remain on the continuation call stack inherited from their preceding async action. The only computations which are shifted are those which are defined as synchronous actions and are contiguous in the bind chain following the shift.

    For example this sample forces an asynchronous boundary (which usually means that the continuation is scheduled for asynchronous execution with setTimeout) before the file will be read synchronously:

    IO.shift().flatMap(_ => fs.readFileSync(path))
    

    On the other hand in this example the asynchronous boundary is inserted after the file has been read:

    IO.of(() => fs.readFileSync(path)).flatMap(content =>
      IO.shift().map(_ => content))
    

    The definition of IO.async is literally:

    source.flatMap(a => IO.shift(ec).map(_ => a))
    

    And the definition of IO.fork is:

    IO.shift(ec).flatMap(_ => source)
    

    Parameters

    • Optional ec: Scheduler

      is the Scheduler used for triggering the async boundary, or if not provided it will default to the scheduler passed on evaluation in IO.run

    Returns IO<void>

Static suspend

  • suspend<A>(thunk: function): IO<A>
  • Promote a thunk function generating IO results to an IO of the same type.

    Type parameters

    • A

    Parameters

    • thunk: function
        • (): IO<A>
        • Returns IO<A>

    Returns IO<A>

Static tailRecM

  • tailRecM<A, B>(a: A, f: function): IO<B>
  • Keeps calling f until a Right(b) is returned.

    Based on Phil Freeman's Stack Safety for Free.

    Described in FlatMap.tailRecM.

    Type parameters

    • A

    • B

    Parameters

    • a: A
    • f: function
        • (a: A): IO<Either<A, B>>
        • Parameters

          • a: A

          Returns IO<Either<A, B>>

    Returns IO<B>

Static unit

  • unit(): IO<void>
  • Shorthand for now(undefined as void), always returning the same reference as optimization.

    Returns IO<void>

Static unsafeStart

  • unsafeStart<A>(source: IO<A>, context: IOContext, cb: function): void | ICancelable
  • Unsafe utility - starts the execution of an IO.

    This function allows for specifying a custom IOContext when evaluating the IO reference.

    DO NOT use directly, as it is UNSAFE to use, unless you know what you're doing. Prefer IO.run instead.

    Type parameters

    • A

    Parameters

    • source: IO<A>
    • context: IOContext
    • cb: function
        • (r: Try<A>): void
        • Parameters

          • r: Try<A>

          Returns void

    Returns void | ICancelable

Legend

  • Module
  • Object literal
  • Variable
  • Function
  • Function with type parameter
  • Index signature
  • Type alias
  • Enumeration
  • Enumeration member
  • Property
  • Method
  • Interface
  • Interface with type parameter
  • Constructor
  • Property
  • Method
  • Index signature
  • Class
  • Class with type parameter
  • Constructor
  • Property
  • Method
  • Accessor
  • Index signature
  • Inherited constructor
  • Inherited property
  • Inherited method
  • Inherited accessor
  • Protected property
  • Protected method
  • Protected accessor
  • Private property
  • Private method
  • Private accessor
  • Static property
  • Static method

Generated using TypeDoc