uncons is a Pull data constructor that stores an underlying source pull instead of stepping it immediately. Its type records that observing the source produces no output and eventually yields either the source result or an output value with a remainder pull. section 15.3
The constructor resolves a tension between ordinary and effectful pulls:
- Calling step immediately would require a Monad constraint while constructing uncons, making the operation unusable for non-effectful Pull values with F = Nothing. section 15.3
- Storing the source defers step until the resulting pull is evaluated, preserving laziness and allowing operations such as take to consume one element at a time. section 15.2.1 section 15.3
- When step encounters Uncons(source), it steps the source and wraps the result in a Left, leaving the constructor itself output-free. section 15.3
Using delayed observation in take
def uncons: Pull[Nothing, Either[R, (O, Pull[O, R])]] = Pull.done >> Result(step) def take(n: Int): Pull[O, Option[R]] = if n <= 0 then Result(None) else uncons.flatMap: case Left(r) => Result(Some(r)) case Right((hd, tl)) => Output(hd) >> tl.take(n - 1) Here, take observes the next source result through uncons rather than eagerly invoking step. The same pattern supports drop, takeWhile, dropWhile, mapOutput, and stateful transformations. section 15.2.1 section 15.6
val done: Pull[Nothing, Unit] = Result(())
Compare the delayed observation pattern with Pull.done, the output-free completed pull used to terminate recursive operations. section 15.2.1
