Skip to content

Vocabularies and models

An article defines its own symbols. What it does not define, such as "probability measure on $[0,1]^2$" or "Lebesgue measure", comes from a vocabulary: a reviewed list of fixed phrases, each with a closed term of the core language. A vocabulary's model gives its base types and constants their Lean meaning. Both are written once, reviewed once and shared by every article that uses them.

The core language

Leanify/Syntax.lean defines the terms (Tm) and types (Ty) that sentences compile to: real numbers, [0,1], propositions, functions, base types Ty.base k supplied by a vocabulary, and a small set of primitives (Prim) with mathlib meanings: real powers, min, max, Lebesgue measure on [0,1], Borel measurability, measurable sets, integrability on [0,1] and derivatives within a set. Leanify/Denote.lean gives every term its Lean denotation, relative to a model. Definite descriptions ("the unique $b$ with …") denote Classical.epsilon, and are well defined when the description has exactly one solution.

A vocabulary

structure Vocab where
  types : List Codes        -- names of the base types (for readings)
  consts : List Ty          -- types of the constants `Tm.const i`
  constNames : List Codes   -- their names (for readings)
  nouns : List Noun         -- "distribution function on $[0,1]^2$"
  mods : List Modifier      -- "Borel measurable", "with uniform marginals"
  notions : List Notion     -- "Lebesgue measure on $[0,1]$"

Phrases are written with phrase!. Words are matched literally, any formula in the phrase is matched token for token, and $v$ marks the variable the phrase binds:

def vocab : Vocab where
  consts := [Ty.sq ⟶ .prop, Ty.sq ⟶ .prop, Ty.meas2 ⟶ .prop, Ty.meas2 ⟶ .prop]
  nouns := [
    { sing := phrase!"distribution function on $[0,1]^2$",
      plural := phrase!"distribution functions on $[0,1]^2$", ty := Ty.sq, pred := .const 0 },
    { sing := phrase!"probability measure $v$ on $[0,1]^2$",
      plural := phrase!"probability measures $v$ on $[0,1]^2$", ty := Ty.meas2, pred := .const 2 }]
  mods := [
    { phrase := phrase!"with uniform marginals", prenominal := false, ty := Ty.sq, pred := .const 1 },
    { phrase := phrase!"with uniform marginals", prenominal := false, ty := Ty.meas2, pred := .const 3 }]

This is Leanify.UnitSquare.vocab. With it, the sentence

let $\mathcal{D}$ denote the class of standard distribution functions, that is, distribution functions on $[0,1]^2$ with uniform marginals.

defines \mathcal{D} as the predicate fun F => DistFn F ∧ UnifMarg F. The full list of its phrases is in Leanify/Library/UnitSquare.lean.

Vocabularies combine with ++ (Vocab.core ++ UnitSquare.vocab): the base types and constants of the second are renumbered after those of the first.

A model

structure Model where
  base : Nat → Type                -- the base types
  default : (k : Nat) → base k     -- an element of each (for totalized terms)
  consts : List (Val base)         -- each constant: its type and its value

The model of the unit-square vocabulary (Leanify/Library/UnitSquareModel.lean) says what the four constants mean in mathlib terms:

def distFn2 (F : I → I → ℝ) : Prop :=
  ∃ μ : Measure (I × I), IsProbabilityMeasure μ ∧
    ∀ u v : I, F u v = (μ (Set.Iic u ×ˢ Set.Iic v)).toReal

def dfUniformMarginals (F : I → I → ℝ) : Prop :=
  (∀ u : I, F u 1 = u) ∧ ∀ v : I, F 1 v = v

Reviewing a vocabulary means checking that each phrase means what its model says. That review is the only place where words acquire meaning. Everything an article adds is defined in the article and checked.

Using a vocabulary

-- Compiled.lean
#leanify_article Foo "article.tex" "fnv1a64:…" with MyVocab.vocab
-- Article.lean
#leanify_semantics Foo "article.tex" with MyVocab.model

The native checker knows the vocabularies it was built with (lake exe leanify check article.tex --vocab unitsquare). A project with its own vocabulary adds a small executable:

import Leanify.Check
import MyProject.Vocab

def main (args : List String) : IO UInt32 := do
  let [path] := args | IO.eprintln "usage: check ARTICLE.tex"; pure 2
  let failed ← Leanify.checkArticle (Leanify.Vocab.core ++ MyProject.vocab) path false true
  pure (if failed == 0 then 0 else 1)

Adding to the core

A phrase belongs in the core vocabulary when it is standard across subjects and has one established mathlib meaning. Subject vocabularies (measures on the unit square, …) live in Leanify/Library/ or in the project that uses them. A new primitive in Prim needs its denotation, its definedness condition and its readable Lean form (Leanify/Readable.lean), and should be rare.