liveBook · Manning

Output

As explained in Functional Programming in Scala, Second Edition

Output is a Pull constructor representing one output value followed by a continuation pull.

Output is a Pull constructor for emitting one value and continuing with the remainder of a pull. Its type is Pull[O, Unit], so it represents one output followed by successful termination; sequence several outputs with >>, which is derived from flatMap. section 15.2 section 15.2.1

Use Output as the small building block for pull construction:

  • Write Pull.Output(value) to emit one value. section 15.2
  • Append another pull with p1 >> p2; this abbreviates p1.flatMap(_ => p2). section 15.2.1
  • Build a pull from a list by recursively emitting its head and continuing with the tail. section 15.2.1
  • Evaluate the constructed pull with fold or collect its emitted values with toList. section 15.2 section 15.2.1

Concatenating two outputs

val p = Pull.Output(1) >> Pull.Output(2) val values = p.toList // List(1, 2) The first Output emits 1; sequencing supplies the second Output as the continuation, and toList evaluates both emissions. section 15.2.1

Listing 15.2 The Pull data type
enum Pull[+O, +R]:
  case Result[+R](result: R) extends Pull[Nothing, R]
  case Output[+O](value: O) extends Pull[O, Unit]
  case FlatMap[X, +O, +R](
    source: Pull[O, X],
    f: X => Pull[O, R]) extends Pull[O, R]
 
  def step: Either[R, (O, Pull[O, R])] = this match            #1
    case Result(r) => Left(r)
    case Output(o) => Right(o, Pull.done)
    case FlatMap(source, f) =>
      source match
        case FlatMap(s2, g) =>
          s2.flatMap(x => g(x).flatMap(y => f(y))).step
        case other => other.step match
          case Left(r) => f(r).step
          case Right((hd, tl)) => Right((hd, tl.flatMap(f)))
 
  @annotation.tailrec
  final def fold[A](init: A)(f: (A, O) => A): (R, A) =         #2
    step match
      case Left(r) => (r, init)
      case Right((hd, tl)) => tl.fold(f(init, hd))(f)
 
  def toList: List[O] =                                        #3
    fold(List.newBuilder[O])((bldr, o) => bldr += o)(1).result
 
  def flatMap[O2 >: O, R2](
    f: R => Pull[O2, R2]
  ): Pull[O2, R2] =                                            #4
    Pull.FlatMap(this, f)
 
  def >>[O2 >: O, R2](next: => Pull[O2, R2]): Pull[O2, R2] =
    flatMap(_ => next)
 
  def map[R2](f: R => R2]): Pull[O, R2] =
    flatMap(r => Result(f(r)))

Inspect the Pull definition to see Output alongside Result, FlatMap, step, and fold. section 15.2

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Defines Output’s type, sequencing, recursive construction, and evaluation, while deferring broader Pull implementation details.
Examples
Demonstrates single outputs, concatenated outputs, list conversion, recursive list construction, and infinite constructors.
What you can do after
Build it from scratch by composing Output values with >> and evaluating the resulting Pull with toList or fold.
How it connects
Connects Output to Pull, flatMap, fold, lazy evaluation, List, LazyList, and stream transformations.

This explainer was generated by Manning's knowledge graph from the text of Functional Programming in Scala, Second Edition. The section links point to the parts of the book it draws on.