Skip to content

Records and schemas

A function that takes a record, a row, a form or a message is claimed over the schema that already describes it. Whatever describes a record names a language of them: a pydantic model, a dataclass, a TypedDict, a SQLAlchemy table, a Django model or a JSON Schema, written by its dotted path, L[shop.db.Order]. A parameter annotated with the class infers its language on its own. Membership is decided by the library's own validator where it has one, so a record is valid exactly when your application's own library says so.

Schema Written Membership decided by
a pydantic model L[shop.forms.SignupForm] model_validate
a SQLAlchemy table or mapped class L[shop.db.Order] inserting the row into in-memory SQLite
a Django model L[shop.reviews.Review] full_clean
a JSON Schema, as a dict L[shop.webhooks.CHARGE_EVENT] the jsonschema validator
a dataclass L[shop.shipping.Parcel] the package's own checker
a TypedDict L[shop.search.SearchHit] the package's own checker

Each has a page of its own under the reference, saying what it reads and a worked claim. The examples here are the shop's orders table, forms and payment webhook.

Proving a claim over a record

Where a function reads numeric fields, and text fields only through len, the derive route lifts each field it reads to a symbol bounded by the schema, and the ordinary prover runs, so a claim over every record is proven rather than sampled:

class Order(Base):
    __tablename__ = "orders"
    id: Mapped[int] = mapped_column(primary_key=True)
    sku: Mapped[str] = mapped_column(sa.String(12), sa.CheckConstraint("length(sku) >= 3"))
    quantity: Mapped[int] = mapped_column(sa.CheckConstraint("quantity BETWEEN 1 AND 100"))
    unit_price: Mapped[Decimal] = mapped_column(sa.Numeric(10, 2),
                                                sa.CheckConstraint("unit_price >= 0"))


def order_total(order: Order) -> Decimal:
    """What the customer pays for the order."""
    return order.quantity * order.unit_price
f = order_total
for order in L[shop.db.Order], f(order) >= 0
    proven

for order in L[shop.db.Order], f(order) <= 10000
    falsified   order = Order(id=0, sku='aaa', quantity=1, unit_price=Decimal('10742.34')): Decimal('10742.34') vs 10000

The table's CHECK constraints bound the quantity and the price from below, so the total is never negative; nothing bounds the price from above. A falsified claim's witness is a real record the database accepts, shrunk until only what the failure needs is left: the sku is the shortest the CHECK allows, the quantity is one.

A schema written as data

A JSON Schema is the language of the documents it validates, which is how a webhook handler meets every event the provider can send:

CHARGE_EVENT = {
    "type": "object",
    "properties": {
        "type": {"enum": ["charge.succeeded", "charge.failed", "charge.refunded"]},
        "amount": {"type": "integer", "minimum": 0},
    },
    "required": ["type", "amount"],
    "additionalProperties": False,
}

QUEUES = {"charge.succeeded": "billing", "charge.failed": "alerts"}


def queue_for(event: dict) -> str:
    """The queue a charge event is routed to."""
    return QUEUES[event["type"]]
f = queue_for
for event in L[shop.webhooks.CHARGE_EVENT], f(event) in {"billing", "alerts"}
    falsified   event = {'type': 'charge.refunded', 'amount': 0}: raised KeyError

Paths into a record

A binding can narrow the records a claim covers by naming a path into them, through fields and indices, at any depth: checkout.postcode, checkout.cart.items[0].quantity, and [*] for every element. A bound on a path means the value is there: a record whose path reaches nothing (a field holding None, a key that is not there, an index past the end) is absent there and outside the bound, and | {absent} keeps it in. Relations and paths has the two kinds of nothing.

class Checkout(BaseModel):
    cart: Cart
    postcode: str | None = Field(default=None, max_length=8)


def needs_postcode(checkout: Checkout) -> bool:
    """Whether the checkout page still has to ask for a postcode."""
    return checkout.postcode is None


def first_quantity(checkout: Checkout) -> int:
    """The quantity of the first item, 0 for an empty basket."""
    items = checkout.cart.items
    return items[0].quantity if items else 0
f = needs_postcode
for checkout in L[shop.forms.Checkout], f(checkout) == False
    falsified   checkout = Checkout(cart=Cart(items=[]), postcode=None): True vs False

for checkout in L[shop.forms.Checkout], checkout.postcode in L[alnum], f(checkout) == False
    holds

f = first_quantity
for checkout in L[shop.forms.Checkout], checkout.cart.items[*].quantity in [1, 3], f(checkout) <= 3
    holds

for checkout in L[shop.forms.Checkout], checkout.cart.items[0].quantity in [7, 7], f(checkout) == 7
    holds

for checkout in L[shop.forms.Checkout], checkout.cart.items[0].quantity in [7, 7] | {absent}, f(checkout) == 7
    falsified   checkout = Checkout(cart=Cart(items=[]), postcode=''): 0 vs 7

The last is falsified by an empty basket: items[0] is past the end of the list, so the path is absent there, and | {absent} brought that checkout into the claim.

Enforcing the schema

A function whose job is to guard a boundary should refuse a record its schema rejects, and excluded_outside_domain feeds it records just outside the language. A witness names the value and why it is outside, in the library's own words:

def welcome_message(form: SignupForm) -> str:
    """The first line of the welcome email."""
    return f"Welcome, {form.username}!"
f = welcome_message
for form in L[shop.forms.SignupForm], excluded_outside_domain(form)
    falsified   form = SignupForm(username='aaa', age=12) (outside L[shop.forms.SignupForm] at .age: Input should be greater than or equal to 13) is outside the declared domain but was accepted

That is fine for a function that trusts pydantic to have validated the form already, and the claim is the one to write for the function that loads it, from a request or a database, where nothing has.

Recipes

To say Write
a computed value stays in range for every record for order in L[shop.db.Order], f(order) >= 0
a handler copes with every document the schema allows for event in L[shop.webhooks.CHARGE_EVENT], is_language_defined(event)
every element of a list field is within bounds for c in L[shop.forms.Checkout], c.cart.items[*].quantity in [1, 3], ...
a field is present, not absent c.postcode in L[alnum]
a boundary refuses what the schema rejects for form in L[shop.forms.SignupForm], excluded_outside_domain(form)