Tabular facts

Daniel edited this page Nov 30, 2013 · 10 revisions
Clone this wiki locally

Sometimes it's convenient to illustrate a fact with a table of values. For example, Conway's Game of Life has rules for when cells are born, stay alive, or die. They look nice expressed as a table:

     (tabular
      (fact "The rules of Conway's life"
        (alive? ?cell-status ?neighbor-count) => ?expected)
      ?cell-status   ?neighbor-count   ?expected
      :alive         1                 FALSEY        ; underpopulation
      :alive         2                 truthy       
      :alive         3                 truthy
      :alive         4                 FALSEY        ; overpopulation

      ;; A newborn cell has three parents
      :dead          2                 FALSEY
      :dead          3                 truthy
      :dead          4                 FALSEY)

Note that you can capitalize falsey. FALSEY and TRUTHY are synonyms for the lowercase form. I find that using the capitalized form for one makes a tabular fact easier to read.

In case of a failure, the line number points to the prediction, as usual. But it's augmented by a description of which set of values caused the failure. For example, if alive? always returns false, the fact above will yield three failures:

     FAIL at (t_sweet.clj:237)
     With table substitutions: {?cell-status :alive, ?neighbor-count 2, ?expected truthy}
     Actual result did not agree with the checking function.
             Actual result: false
         Checking function: truthy
     FAIL at (t_sweet.clj:237)
     With table substitutions: {?cell-status :alive, ?neighbor-count 3, ?expected truthy}
     Actual result did not agree with the checking function.
             Actual result: false
         Checking function: truthy
     FAIL at (t_sweet.clj:237)
     With table substitutions: {?cell-status :dead, ?neighbor-count 3, ?expected truthy}
     Actual result did not agree with the checking function.
             Actual result: false
         Checking function: truthy

Details

Substitutions work with more than just plain values. For example, instead of using truthy and falsey, you can substitute the checking arrow:

     (tabular
      (fact "only two numbers have the same sum and square"
        (* ?n ?n) ?arrow (+ ?n ?n))
      ?n        ?arrow
      0         =>      
      2         =>
      ;; Failure cases
      1         =not=>
      ;; and so on
      )

Tabular facts behave exactly like regular facts. They can be turned into future facts:

     (tabular
      (future-fact (inc ?int) => ?int)
      ?int
      1)

They obey fact-wide prerequisites, setup/teardown, and so on.

You can have many checkables within the fact.

Doc strings (and other metadata) can be put with either the tabular or the fact.

     (tabular "increment works"
      (fact (inc ?int) => ?int)
      ?int
      1)