Tutorial 3 — Writing Specifications¶
A spec is a logical statement that constrains how relations behave. Specs are the semantic core of a len model — they express the laws, definitions, and invariants that must hold.
This tutorial shows you how to write specs for a domain model and use the full range of logical connectives.
The spec Form¶
givenintroduces universally-bound variables — they range over all values of their type.muststates the formula that must hold.
A spec with no given clause states an unconditional axiom:
Naming Conventions for Specs¶
Prefix the spec name to signal its role:
| Prefix | Role |
|---|---|
| (no prefix) | General law or constraint |
axiom_* |
Fundamental assumption |
definition_* |
Introduces a new notion in terms of existing ones |
theorem_* |
Derivable from other specs |
lemma_* |
Auxiliary statement for a proof |
corollary_* |
Follows directly from a theorem |
postulate_* |
Local primitive assumption |
Logical Connectives¶
and — Conjunction¶
Both sides must hold:
or — Disjunction¶
At least one side must hold:
not — Negation¶
The formula must not hold:
implies — Implication¶
If the left holds, the right must hold too:
iff — Biconditional¶
Both directions of implication — a definition-style law:
Quantifiers¶
forall — Universal¶
The formula must hold for every value:
The . separates the binders from the formula body.
Multiple variables of the same type can be declared together:
exists — Existential¶
At least one value must satisfy the formula:
Combine forall and exists freely:
Equality¶
Use = to state that two things are identical:
spec customer_email_unique
must forall c1, c2: Customer, e: Email .
CustomerEmail(c1, e) and CustomerEmail(c2, e) implies c1 = c2
Syntax Sugar¶
If you import core.math.logic.sugar you can use shorthand operators:
| Sugar | Keyword | Meaning |
|---|---|---|
A => B |
implies |
Implication |
A <=> B |
iff |
Biconditional |
A /\ B |
and |
Conjunction |
A \/ B |
or |
Disjunction |
!A |
not |
Negation |
Building a Spec Suite for the Shop Model¶
Continuing from the shop model in Tutorial 2, add a specs.l1 file:
import shop.types
import shop.catalogue
import shop.orders
# Every product has exactly one name
spec product_name_unique
must forall p: Product, a, b: String .
ProductName(p, a) and ProductName(p, b) implies a = b
# Every product has exactly one price
spec product_price_unique
must forall p: Product, a, b: Money .
ProductPrice(p, a) and ProductPrice(p, b) implies a = b
# Every order must have a customer
spec order_has_customer
must forall o: Order .
exists c: Customer . OrderCustomer(o, c)
# An order may only be shipped if it has been paid
spec shipped_implies_paid
given o: Order
must Shipped(o) implies Paid(o)
# An order may only be paid if it has at least one item
spec paid_implies_has_items
given o: Order
must Paid(o) implies
exists item: OrderItem . OrderContains(o, item)
# A cancelled order is neither paid nor shipped
spec cancelled_is_terminal
given o: Order
must Cancelled(o) implies
not Paid(o) and not Shipped(o)
Run validation:
What You Learned¶
specdeclares a logical law usinggiven(binders) andmust(the formula)and,or,not,implies,iffare the core connectivesforallandexistsexpress statements about all or some values=states identity- Naming conventions (
definition_*,axiom_*, etc.) communicate the role of each spec - Sugar operators (
=>,/\, etc.) are available throughcore.math.logic.sugar
Next: Functions and Quasi →