Pull

sealed abstract class Pull[+F[_], +O, +R]

A purely functional data structure that describes a process. This process may evaluate actions in an effect type F, emit any number of output values of type O (or None), and may a) terminate with a single result of type R; or b) terminate abnormally by raising (inside the effect F) an exception, or c) terminate because it was cancelled by another process, or d) not terminate.

A purely functional data structure that describes a process. This process may evaluate actions in an effect type F, emit any number of output values of type O (or None), and may a) terminate with a single result of type R; or b) terminate abnormally by raising (inside the effect F) an exception, or c) terminate because it was cancelled by another process, or d) not terminate.

Like types from other effect libraries, pulls are pure and immutable values. They preserve referential transparency.

=== Chunking ===

The output values of a pull are emitted not one by one, but in chunks. A Chunk is an immutable sequence with constant-time indexed lookup. For example, a pull p: Pull[F, Byte, R] internally operates and emits Chunk[Byte] values, which can wrap unboxed byte arrays -- avoiding boxing/unboxing costs. The Pull API provides mechanisms for working at both the chunk level and the individual element level. Generally, working at the chunk level will result in better performance but at the cost of more complex implementations

A pull only emits non-empty chunks.

However, chunks are not merely an operational matter of efficiency. Each pull is emitted from a chunk atomically, which is to say, any errors or interruptions in a pull can only happen between chunks, not within a chunk. For instance, if creating a new chunk of values fails (raises an uncaught exception) while creating an intermediate value, then it fails to create the entire chunk and previous values are discarded.

=== Evaluation ===

Like other functional effect types (e.g. cats.effect.IO), a pull describes a process or computation. It is not a running process nor a handle for the result of a spawned, running process, like scala.concurrent.Future.

A pull can be converted to a stream and then compiled to an effectful value. For a Pull[F, O, Unit], the result of compilation is a combination, via the monad instance of F, of all the actions in the effect F present in the pull. The result of that F action is the result of combining the outputs emitted by the pull, in the order it emits them, using a fold function. Depending on that function, outputs may be collected into a list (or vector or array or ...), combined together into a single value, or just discarded altogether (by draining the pull).

Compilation is pull-based, rather than push-based (hence the name of the datatype). It is the compilation process itself, that determines when the evaluation of each single effect can proceed or is held back. Effects and outputs later in the pull are not performed or emitted, unless and until the compilation calls for them.

=== Resource scoping ===

The effects in a Pull may operate on resources, which must be retained during the execution of the pull, may be shared by several pulls, and must be properly finalised when no longer needed, regardless of whether the pull completed successfully or not. A pull tracks its resources using '''scopes''', which register how many pulls are actively using each resource, and finalises resources when no longer used.

Some operations of the Pull API can be used to introduce new resource scopes, or resource boundaries.

=== Functional typeclasses ===

The Pull data structure is a "free" implementation of Monad and has an instance for cats.effect.kernel.Sync.

For any types F[_] and O, a Pull[F, O, *] holds the following laws:

  • pure >=> f == f
  • f >=> pure == f
  • (f >=> g) >=> h == f >=> (g >=> h) where f >=> g is defined as a => a flatMap f flatMap g
  • handleErrorWith(raiseError(e))(f) == f(e)
Type Params
F[_]

the type of effect that can be performed by this pull. An effect type of Nothing, also known in fs2 by the alias Pure, indicates that this pull perform no effectful actions. Note: Nothing is a polykinded type, so it can also be applied as an argument to the type parameter F[_].

O

The outputs emitted by this Pull. An output type of Nothing means that this pull does not emit any outputs.

R

The type of result returned by this Pull upon successful termination. An output type of Nothing indicates that this pull cannot terminate successfully: it may fail, be cancelled, or never terminate.

Companion
object
class Object
trait Matchable
class Any

Value members

Concrete methods

def >>[F2[x], O2 >: O, S](post: => Pull[F2, O2, S]): Pull[F2, O2, S]

Lazily appends the given post pull, to be run after this pull.

Lazily appends the given post pull, to be run after this pull.

  • If this pull succeeds, then its result is discarded, the post pull is built, and starts running. The result of post is the result of the composed pull.

  • If this pull raises an error or is interrupted, the post argument is not evaluated and the composed pull ends just as this pull did.

In both cases, the effectful actions and outputs of the appended pull consists of those outputs and actions from the first pull, followed by those from the post pull, in the same order as they would come out of each pull.

Since the post argument is lazy, this method can be used to build lazy pulls, which are not built in memory until after the prefix has run. This allows defining pulls recursively.

This operation does not add or remove any resource scope boundaries. The post pull runs on the same scope in which this pull ended. The composed pull ends on whatever scope the post pull does.

This is equivalent to .flatMap(_ => post)

def as[S](s: S): Pull[F, O, S]

Replaces the result of this pull with the given constant value. If this pull succeeds, then its result is discarded and the resulting pull succeeds with the s value as its result. Otherwise, if this pull fails or is interrupted, then the result pull ends the same way.

Replaces the result of this pull with the given constant value. If this pull succeeds, then its result is discarded and the resulting pull succeeds with the s value as its result. Otherwise, if this pull fails or is interrupted, then the result pull ends the same way.

Alias for _.map(_ => o2).

Type Params
S

The type of the constant,

Value Params
s

The new result value of the pull

def attempt: Pull[F, O, Either[Throwable, R]]

Returns a pull with the result wrapped in Right, or an error wrapped in Left if the pull has raised an error. If this pull is interrupted, the attempted pull ends the same way.

Returns a pull with the result wrapped in Right, or an error wrapped in Left if the pull has raised an error. If this pull is interrupted, the attempted pull ends the same way.

def covary[F2[x]]: Pull[F2, O, R]

Short-hand for (this: Pull[F2, O, R]). Used to assist with type inference.

Short-hand for (this: Pull[F2, O, R]). Used to assist with type inference.

def covaryAll[F2[x], O2 >: O, R2 >: R]: Pull[F2, O2, R2]

Short-hand for (this: Pull[F2, P, S]). Used to assist with type inference.

Short-hand for (this: Pull[F2, P, S]). Used to assist with type inference.

def covaryOutput[O2 >: O]: Pull[F, O2, R]

Short-hand for (this: Pull[F, O2, R]). Used to assist with type inference.

Short-hand for (this: Pull[F, O2, R]). Used to assist with type inference.

def covaryResult[R2 >: R]: Pull[F, O, R2]

Short-hand for (this: Pull[F, O, R2]). Used to assist with type inference.

Short-hand for (this: Pull[F, O, R2]). Used to assist with type inference.

def flatMap[F2[x], O2 >: O, R2](f: R => Pull[F2, O2, R2]): Pull[F2, O2, R2]

Applies the result of this pull to f and returns the result.

Applies the result of this pull to f and returns the result.

This method returns a new composed pull, which will do as follows:

  • If this pull succeeds with a result r of type R, the f function is applied to r, to build a new pull f(r), and the result pull starts running that new pull. The composed pull will terminate (or not) just as the new pull f(r) does.
  • If this pull fails or is interrupted, then the composed pull terminates with that same failure or interruption.
  • If evaluating f(r) to build the throws an exception, the result is a pull that fails with that exception.

The composed pull emits all outputs emitted by this pull, and if successful will start emitting the outputs from the generated pull.

This operation does not modify resource scope boundaries. The generated post pull starts running on the same scope in which this pull ended, and the composed pull will end on the same scope in which post pull does.

def handleErrorWith[F2[x], O2 >: O, R2 >: R](handler: Throwable => Pull[F2, O2, R2]): Pull[F2, O2, R2]

Allows to recover from any error raised by the evaluation of this pull. This method returns a composed pull with the following semantics:

Allows to recover from any error raised by the evaluation of this pull. This method returns a composed pull with the following semantics:

  • If an error occurs, the supplied function is used to build a new handler pull, and it starts running it. However, the pull cannot be resumed from the point at which the error arose.
  • If no error is raised, the resulting pull just does what this pull does.
def map[S](f: R => S): Pull[F, O, S]

Maps the result of this pull with the f mapping funciton.

Maps the result of this pull with the f mapping funciton.

If this pull ends in success with a result r, then the function f is applied to its result r, and the image f(r) is the result of the mapped pull. However, if the evaluation of f(r) throws an error, the mapped pull fails with that error.

Note: for some simple cases of Pull, the map function may be eagerly applied, or discarded, before the pull starts being run.

If this pull terminates abnormally, so does the mapped pull.

def onComplete[F2[x], O2 >: O, R2](post: => Pull[F2, O2, R2]): Pull[F2, O2, R2]

Run post after this, regardless of errors during this:

Run post after this, regardless of errors during this:

  • If this pull terminates successfully, then its result is discarded and the post pull is run. However the post pull ends, be it in success, error, interruption, is how the combined pull ends.

  • If this pull fails, the post pull is run next. If the post pull ends fails or is interrupted, that is how the combined pull ends. However, if the post pull succeeds, then the combined onComplete pull fails again with the error that was raised from this pull.

  • If this pull is interrupted, then the post error is never run and the combined pull ends with that same interruption.

def void: Pull[F, O, Unit]

Discards the result of this pull.

Discards the result of this pull.

If this pull ends in success, its result is discarded and the voided pull returns the unit () value. Otherwise, the voided pull just does the same as this pull does.

Alias for this.map(_ => () ).