-
Notifications
You must be signed in to change notification settings - Fork 129
Tabular facts
Sometimes it's convenient to illustrate a fact with a table of values. For example, Conway's 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
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 predictions within the fact
.
Doc strings (and other [metadata]) can be after the tabular
if you find that more readable than after the fact
.
(tabular "increment works"
(fact (inc ?int) => ?int)
?int
1)