Skip to content

Metamodel ​

Goal

Define an architecture to represent a breadth of real-world entities as structured data (models). Specify two operations to act on models: reading and writing. Design a rich system of types, constraints, and invariants, such that any operation always yields a semantically correct model.

We will define the following concepts:

  • values
  • models
  • identifiers
  • references

Labels ​

Going forward, we will need to assign labels to values. To be able to refer to labels in mathematical notation, we define the set L of all labels. By convention, we will understand strings in formulas typeset in monospace font to be elements of L, like name or id.

Values ​

We denote the set of all model values V. There are different kinds of values:

Numbers

A number is any of the rational numbers, which we denote Q.

Examples:

  • −3
  • 0
  • 42
  • 113
  • 9223372036854775808
Bags

A bag is a finite multiset of values. We denote the set of bags over a set X as B(X).

Examples:

  • {1,2,1,2}
  • {42}
  • {}
  • {{0,{0},0,1},{1,1},0}
Lists

A list is a finite sequence of values. We denote the set of lists over a set X as X∗.

Examples:

  • (1,2,3,4)
  • (5,0,5)
  • ()
  • ((1),0,{1})
Records

Records assign labels to values. They are finite, partial functions from labels to values. We denote the set of records over a set X as [L⇀finX].

Examples:

  • [count↦42]
  • [dice_rolls↦{3,3,1,6},ball_picks↦(0,0,1,1,0)]

Layered Construction ​

The value kinds above are mutually recursive: bags, lists, and records contain values, which themselves may be bags, lists, or records. To give this a well-defined foundation, we construct V in layers.

Definition (Value Hierarchy). We define an ascending sequence of sets (Vn)n∈N by induction:

V0:=QVn+1:=Vn⊔B(Vn)⊔Vn∗⊔[L⇀finVn]

Each layer Vn+1 contains all values from the previous layer Vn, plus all bags, lists, and records formed from those values.

Definition (Value Domain). The set of all values is the union of all layers:

V:=⋃n∈NVn

Theorem (Fixed Point). V satisfies the equation:

V=Q⊔B(V)⊔V∗⊔[L⇀finV]

Proof sketch.

  • (⊇): Numbers are in V0⊆V. Any bag, list, or record over V involves finitely many values, all lying in some layer VN. Thus it belongs to VN+1⊆V.
  • (⊆): By induction, each Vn⊆Q⊔B(V)⊔V∗⊔[L⇀finV], so their union V is contained in the right-hand side. ∎

Theorem (Well-Foundedness). Every value v∈V has a unique minimal layer ℓ(v)∈N, the least n such that v∈Vn. If v is a compound value (bag, list, or record), all its immediate constituents have strictly smaller layer: ℓ(constituent)<ℓ(v).

This ensures the axiom of regularity: there are no infinite descending chains of membership.

Models ​

Model Part ​

A model part assigns labels to data, thereby describing the data’s meaning. Formally, we define a model part as a function into T whose domain is a non-empty, finite set of labels. We denote the set of all model parts M.

M:={L→T∣L⊂L∧|L|∈N+}

Type System ​

Overview ​

A type system classifies values into types, enabling static reasoning about program behavior. In our metamodel, types are defined as subsets of the value domain V, and subtyping is derived from set inclusion.

Type Syntax ​

We define the set of types T inductively as the smallest set closed under the following constructors:

T:=Num∣Int∣T(v)∣Bag(T)∣List(T,T)∣Cons(T,T)∣Record(l1↦T,…,ln↦T)∣ClosedRecord(l1↦T,…,ln↦T)∣[a,b]incl1,incl2∣Mult(T,T)∣T∪T∣T∩T∣T∖T∣μα.T∣μ(α1,…,αn).(τ1,…,τn)

where:

  • v∈V
  • l1,…,ln∈L are distinct labels
  • a,b∈Q∪{−∞,+∞}
  • incl1,incl2∈{open,closed}
  • α,α1,…,αn are type variables
  • τ1,…,τn are types that may reference the bound type variables

Primitive Types ​

  • Num: All rational numbers, [[Num]]=Q
  • Int: All integers, [[Int]]=Z

Singleton Types ​

For each value v∈V, the singleton type T(v) contains exactly that value:

[[T(v)]]={v}

Parameterized Types ​

Bag type: Bag(T) represents bags where every element conforms to type T:

[[Bag(T)]]=B([[T]])

List type: List(T,L) represents lists where every element conforms to type T and the length belongs to type L:

[[List(T,L)]]={l∈V∗∣∀i∈{0,…,|l|−1}.l[i]∈[[T]]∧|l|∈[[L]]}

where L is a type representing non-negative integers (e.g., an interval type).

Record type: Record(l1↦T1,…,ln↦Tn) represents open records with specified key types:

[[Record(l1↦T1,…,ln↦Tn)]]={r∈[L⇀finV]∣∀i∈{1,…,n}.r(li)∈[[Ti]]}

Open records support width subtyping: a record with additional keys is a subtype.

Closed record type: ClosedRecord(l1↦T1,…,ln↦Tn) represents records with exactly the specified keys:

[[ClosedRecord(l1↦T1,…,ln↦Tn)]]={r∈[L⇀finV]∣dom(r)={l1,…,ln}∧∀i∈{1,…,n}.r(li)∈[[Ti]]}

Sequence Types ​

Cons type: Cons(A,B) represents the concatenation of two sequence types:

[[Cons(A,B)]]={concat(a,b)∣a∈[[A]]∧b∈[[B]]}

This constructor requires that A and B are sequence types (bags or lists). The concatenation operation concat is defined for compatible sequence kinds.

Interval Types ​

Interval types represent subsets of Q bounded by rational numbers or infinity:

[[[a,b]incl1,incl2]]={q∈Q∣a≤q≤b}

with the constraint that inclusive bounds must be finite: if incl1=closed, then a∈Q; if incl2=closed, then b∈Q.

Notation: [a,b] for both closed, (a,b) for both open, [a,b) for left closed and right open, (a,b] for left open and right closed.

Numeric Types ​

Multiplication type: Mult(A,B) represents the set of all products of values from A and B:

[[Mult(A,B)]]={a×b∣a∈[[A]]∧b∈[[B]]}

This constructor requires that A and B are numeric types (subtypes of Num).

Set Operations ​

Union: T1∪T2 represents the union of two types:

[[T1∪T2]]=[[T1]]∪[[T2]]

Intersection: T1∩T2 represents the intersection of two types:

[[T1∩T2]]=[[T1]]∩[[T2]]

Set difference: T1∖T2 represents the set difference:

[[T1∖T2]]=[[T1]]∖[[T2]]

Recursive Types ​

Single Recursive Types ​

A recursive type μα.τ binds a type variable α within the body τ, allowing τ to reference the type being defined.

Semantics: The semantic function maps recursive types to the least fixed point of a recursive equation:

[[μα.τ]]=lfp(X↦[[τ[X/α]]])

where lfp denotes the least fixed point, and τ[X/α] denotes substitution of X for α in τ.

Example: A tree type:

Tree=μα.Record(value↦Int,children↦Bag(α))

The semantic equation is:

X={r∈[L⇀finV]∣r(value)∈Z∧r(children)∈B(X)}

The least fixed point is the set of all finite trees with integer values.

Base cases: A recursive type is inhabited if and only if its semantic equation has a non-empty least fixed point. This requires the type to have base cases that allow the recursion to terminate with finite, well-founded values.

Example (inhabited): μα.Record(child↦α∪T(null)) has base case null, so it's inhabited by all finite chains ending in null.

Example (uninhabited): μα.Record(child↦α) has no base case, so its least fixed point is ∅.

Mutually Recursive Types ​

Mutually recursive types are defined as a simultaneous fixed point of multiple type equations:

μ(α1,…,αn).(τ1,…,τn)

Semantics: The semantic function maps this to the least fixed point of the system of equations:

[[μ(α1,…,αn).(τ1,…,τn)]]=lfp((X1,…,Xn)↦([[τ1[X→/α→]]],…,[[τn[X→/α→]]]))

Example: Doubly-linked list nodes:

μ(α,β).(Record(value↦Int,next↦β∪T(null)),Record(value↦Int,prev↦α∪T(null)))

This defines two types α and β that reference each other.

Subtyping ​

Subtyping is derived from set inclusion of the semantic function:

T1≤T2⟺[[T1]]⊆[[T2]]

Examples:

  • Int≤Num because Z⊆Q
  • T(42)≤Int because {42}⊆Z
  • Record(a↦Int,b↦\tstring)≤Record(a↦Int) (width subtyping for open records)
  • Int∪\tstring≤Num∪\tstring because Z∪S⊆Q∪S

Type Equivalence ​

Two types are equivalent if they have the same semantics:

T1≡T2⟺[[T1]]=[[T2]]

Typing Relation ​

A value v has type T if and only if v belongs to the semantic interpretation of T:

v:T⟺v∈[[T]]