liveBook · Manning

Prop

As explained in Functional Programming in Scala, Second Edition

Prop is a book-defined representation of a testable property as a computation over test configuration and random state that produces a result.

Prop represents a testable claim without immediately printing a report or otherwise discarding its outcome. A property is run later with explicit test configuration and random state, producing a Result: Passed, Falsified with a failing case and success count, or Proved. Keeping that result as data makes properties reusable and composable. section 8.1.3 section 8.1.6

A Boolean-returning check is enough to sketch conjunction, but not enough to explain a failure. Prop therefore carries failure information rather than treating execution as the representation. Its refined form is a function from maximum size, number of test cases, and RNG to Result; each dependency required for generating and evaluating cases is explicit. section 8.1.3 section 8.2

This representation supports several distinct kinds of property:

  • Generated properties: forAll turns a Gen or SGen plus a predicate into a Prop, stopping when a generated value falsifies the predicate or throws an exception. section 8.1.6
  • Combined properties: && runs the right property only after the left passes, while || tries the right property after a left failure; tags can preserve the failure path. section 8.6
  • Directly verified properties: verify evaluates a hardcoded Boolean once and can report Proved rather than pretending it survived many random tests. section 8.2.3
  • Sized properties: Prop distributes testing across sizes up to a maximum, allowing SGen-based tests to seek smaller failing cases. section 8.2

Specifying List.max

Generate lists of small integers and assert that their maximum is at least every element. The first version exposes an unstated precondition: max fails on an empty list. Switching to a nonempty-list generator makes the property state the intended domain as well as the expected result. section 8.2.2 section 8.6

Testing laws for parallel computations

Prop can express both a fixed equality, such as mapping +1 over Par.unit(1), and a generated law over arbitrary integers. Helper generators for ExecutorService and Par values move setup details behind forAllPar, leaving the law visible. section 8.2.3

Listing 8.3 Implementing forAll
def forAll[A](as: Gen[A])(f: A => Boolean): Prop =
  (n, rng) =>
    randomLazyList(as)(rng)
      .zip(LazyList.from(0))
      .take(n)
      .map:                                                #1
        case (a, i) =>
          try
            if f(a) then Passed
            else Falsified(a.toString, i)                  #2
          catch
            case e: Exception =>
              Falsified(buildMsg(a, e), i)                 #3
      .find(_.isFalsified)
      .getOrElse(Passed)
}
 
def randomLazyList[A](g: Gen[A])(rng: RNG): LazyList[A] =
  LazyList.unfold(rng)(rng => Some(g.run(rng)))            #4
 
def buildMsg[A](s: A, e: Exception): String =
  s"test case: $s\n" +                                     #5
  s"generated an exception: ${e.getMessage}\n" +
  s"stack trace:\n ${e.getStackTrace.mkString("\n")}"

The forAll implementation shows the operational core: generate a lazy stream from an RNG, test at most n cases, and return the first falsification or Passed. section 8.1.6

How Functional Programming in Scala, Second Edition covers it

How deep it goes
Derives Prop from an inadequate side-effecting check through Result, explicit RNG and size dependencies, combinators, and a forAll implementation.
Examples
Uses List.max, List.sorted, Boolean conjunction, list reversal and sum, and laws for Par computations.
What it assumes
Builds from concrete Boolean checks and generators, while relying on Scala functions, lazy lists, exceptions, and opaque types in implementations.
What you can do after
Build it from scratch as a configurable property runner with generated tests, failure diagnostics, logical combinators, and proved results.
How it connects
Connects Prop to Gen, SGen, RNG, Result, property-based testing, test case minimization, and parallel computations.

Related concepts

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.