Test expression semantics
April 23, 2026 · View on GitHub
This page formally defines what a query produces, what a test compares, and under what conditions a test passes or fails. It is the reference counterpart to ../getting-started/reading-a-test.md, which introduces the same material gently.
What a query produces
A query is written as an expression followed by ?:
E ?
Running a query on expression E produces a query result: a pair
( FactsSet , FactsIsClosed , FalsehoodsSet , FalsehoodsIsClosed )
where:
FactsSetis the set of bindings ofE's free variables for whichEhas been proven to be a factFalsehoodsSetis the set of bindings for whichEhas been proven to be a falsehoodFactsIsClosedistrueiff the reasoner certifies thatFactsSetcontains every binding that makesEa factFalsehoodsIsClosedis defined analogously for falsehoods
These two "closed" flags are what the incompleteness marker .. reports in textual form. A side is closed iff its printed form omits ..; a side is open iff its printed form includes ...
A binding that is in neither set, on a side that is closed, has been affirmatively excluded from that side. A binding that is in neither set, on a side that is open, has no claim made about it.
Printed form
Query results are printed as:
[ facts-list ][ falsehoods-list ]
where each list is a comma-separated sequence of bindings, optionally ending in .. to indicate that side is open.
A binding is written as (v1=value1, v2=value2, ...) or as () for the empty binding (a solution with no free variables). An empty list [] is the closed claim "no bindings on this side."
Examples of printed forms
| Printed form | FactsSet | Facts closed? | FalsehoodsSet | Falsehoods closed? |
|---|---|---|---|---|
[()][] | {()} | yes | {} | yes |
[][()] | {} | yes | {()} | yes |
[..][..] | {} | no | {} | no |
[(f=5)][..] | {(f=5)} | yes | {} | no |
[(a=T1),(a=T2)][..] | {(a=T1),(a=T2)} | yes | {} | no |
[][..] | {} | yes | {} | no |
[(a=0),..][..] | {(a=0)} | no | {} | no |
What a test compares
A test is written as:
E ? [ expected-facts ][ expected-falsehoods ]
The engine runs the query E ? to get an actual result, and compares the actual result to the expected result expressed by the two bracket literals.
The test passes iff both of the following hold:
expected-factsmatchesactualFactsSetand its closure flagexpected-falsehoodsmatchesactualFalsehoodsSetand its closure flag
"Matches" is defined per-side as follows.
Matching a side
Let expected be one bracket literal and actual be the corresponding actual side of the query result.
- If
expectedis closed (does not contain..), it matches iffexpectedis the same set of bindings asactual, andactualis also closed. - If
expectedis open (contains..), it matches iff:- every binding listed explicitly in
expectedis present inactual, and - any remaining bindings in
actualare allowed (no enumeration constraint beyond the listed ones).
- every binding listed explicitly in
In short: a closed bracket asserts an exact set; an open bracket asserts a lower bound (the listed bindings must be present; more are permitted).
Why both sides are checked
A test that only asserts the facts side (by using [..] on the falsehoods side) is not checking falsehoods at all. Conversely, [][(a=T1)] asserts a specific falsehood and an empty, closed facts side. Both sides are first-class; a test expresses whatever facts-and-falsehoods claims the author intends.
Worked examples
fib(5)=f ? [(f=5)][..]
Actual result: FactsSet = {(f=5)}, facts closed, FalsehoodsSet = {}, falsehoods open.
Checking:
- Expected facts
[(f=5)]is closed. Equals{(f=5)}, and actual facts is closed. ✓ - Expected falsehoods
[..]is open with no listed bindings. Every listed binding (none) is in actual. ✓
Passes.
m(Amalia)=Maxima ? [()][]
Actual result: FactsSet = {()}, facts closed, FalsehoodsSet = {}, falsehoods closed.
Checking:
- Expected facts
[()]is closed. Equals{()}, and actual is closed. ✓ - Expected falsehoods
[]is closed. Equals{}, and actual is closed. ✓
Passes.
unknown ? [..][..]
Actual result: FactsSet = {}, facts open, FalsehoodsSet = {}, falsehoods open.
Checking:
- Expected facts
[..]is open with no bindings. Actual has no bindings. ✓ - Expected falsehoods
[..]is open with no bindings. Actual has no bindings. ✓
Passes.
a>0 ? [..][(a=0),..]
From integersTest.nl. Actual result: FactsSet = {} but open (there are infinitely many positive integers, so the reasoner cannot close the side); FalsehoodsSet = {(a=0)} open (there are other falsifying bindings too: (a=-1), (a=-2), ...).
Checking:
- Expected facts
[..]open with no listed bindings. ✓ - Expected falsehoods
[(a=0),..]open with(a=0)listed.(a=0)is in actual. ✓
Passes.
A failing test (hypothetical)
If we changed the last test to a>0 ? [..][(a=1),..], it would fail: (a=1) is a fact side binding of a>0, not a falsehood. The expected falsehoods list asserts (a=1) is a falsehood, but the actual falsehoods side does not contain it.
The relationship to the three-valued model
Given a binding b of the free variables, the query result classifies b as follows:
bin actual facts ⇒bis a factbin actual falsehoods ⇒bis a falsehoodbin neither, both sides closed ⇒ impossible (would mean the reasoner assertsbis neither, but has certified completeness, yetbis a syntactically valid binding — treated as a programming error)bin neither, at least one side open ⇒b's status is unknown (the open side makes no claim)
See three-valued-logic.md for the semantic model behind this.
Bare queries
A query without expected brackets (E ?) does not pass or fail; it just prints its result. Bare queries are useful during development. A file of bare queries (like queryOnly.nl) runs and produces a transcript, which you can read, compare to your expectations, and then turn into tests by adding brackets.
See also
../getting-started/reading-a-test.md— gentle introduction to the same materialthree-valued-logic.md— what facts, falsehoods, and unknown meanwriting-rules.md— how rules produce the facts and falsehoods that tests observe