2026-09-18 Jigsawing an extended System-F calculus
I have been rather slow in coming up with new posts for several reasons, the
most important being that I don’t think what I can come up with is anything
worth writing about. This attitute hasn’t been really productive because the
focus remains on having something new or fresh every single time I write.
To break out of this rut, I thought I would follow the advice given by my
supervisor to all of us in our research group and try something new: put up quick-and-dirty drafts, half-baked ideas, something sketched out for
things I learn, slowly figure out, test or just play with. In short, convey the
fun and frustration of working with programming language ideas.
In each case, the individual terms have different types but the
shape of the result is the same:
for the first, it’s a term of the same type as the input
for the second, it’s the first input term and it’s type
for the third, it’s a the result of the composition of two functions where one
takes the output of another; the output type is that of the first function (f)
Taking the first lambda term and plugging in any type for a variable would
univeralize the type. Mathematically:
∀τ.λx:τ.x
encompasses any input type for the term x in the entire lambda expression.
This is what System-F does.
The ∀α.τ
is universal quantification over types where for
terms, it is Λα.e
. e[τ]
is a typeτ
applied to the
term akin to how ee
applies one term to another.
Note that the contexts now store not just typed terms but also individual types.
Typing
The typing rules for System-F are the rules for the simply typed lambda calculus:
[T-Var]
Γ⊢x:τx:τ∈Γ
[T-Abs]
Γ⊢λx:τ1.e:τ1→τ2Γ,x:τ1⊢e:τ2
[T-App]
Γ⊢e1e2:τ2Γ⊢e1:τ1→τ2Γ⊢e2:τ1
to which the following are added:
[T-TAbs]
Γ⊢Λα.e:∀α.τΓ,α⊢e:τα∈/Γ
Here given e is type-checked in a context that is extended with the type
α. α is abstracted over in the expression e i.e.
α is a placeholder for a type within e and can be replaced by any
type that makes e type check to τ
,
[T-TApp]
Γ⊢e[τ]:{α↦τ}τ′Γ⊢e:∀α.τ′
With terms abstracted over types, the T-TApp rule applies specific types to
such terms. It’s important to note that the resulting type of the overall term
never changes (τ′
remains τ′
). It’s only an abstract type within e,
α
, that gets replaced by an actual one α
to produce a resulting term.
The new idea in the paper is to bring in linear types via kinds.
I should write an entirely new note on
kinds but for our purpose
here, simply put, a kind system classifies types.
For simple types that can be constructed
without the input of any other types e.g. Nat, Bool, the kind is ⋆
.
A type constructor that takes one input type
In OCaml, a type constructor
for constructing lists that takes in a type, say ’a and kind ⋆
, to
construct a list
(of type ’a list and kind ⋆
) has kind ⋆→⋆
.
For a function type is of α→β
where α
is the input type and β
is the output type.
When the function is applied to a value of type
α
(kind ⋆
), the result is of
type β
and kind ⋆
.
For the function constructor itself of type α→β
, the kind
is a constructor that first takes in a type α
(kind ⋆
) to return
another constructor that takes in a type β
(again kind ⋆
) to
finally return a type of kind ⋆
. So the kind for the entire
constructor is ⋆→⋆→⋆
In F∘
, there are two kinds instead of the one base kind in System-F.
The second kind is:
∘
that denotes linearity. Linear types encompass values that are consumed only
once i.e. used only once in a computation.
(I should also write another note/post on sub-structural types of which linear
types are an example.)
The kind ⋆
continues to denote types that are not linear.
The sets of types and kinds now are:
τ::=α∣τ1→τ2∣∀α.τκ::=⋆∣∘
Since the context is constructed as a set of variable and types, let’s perform a
small transformation to its form. The set of all types in a context can be considered
as a set with two sorts:
Γtypes::={(τ:⋆),(τ:∘)}
With variables having type τ
and kind κ
, the set of variables can be
binned into the two sorts as:
Γvars::={(x:τ⋆),(x:τ∘)}
Now the context is:
Γ::=⋅∣(Γtypes+Γvars)which can be rewritten asΓ::=⋅∣{(τ:⋆),(τ:∘),(x:τ⋆),(x:τ∘)}
When reading through the paper, I could not help being reminded of something
from my childhood: jigsaw
puzzles. I had a few, one of them
actually a GI Joe-themed one (I actually found a vintage puzzle set which is an exact
copy of the one I had), all of which were valiant efforts by my parents to keep
my occupied and hence quiet.
My strategy to solve them was to find a color or shape or some combination of both
to start with. A few pieces that seemed easier to put together, a bit more
obivous than others and then slowly build out from that nucleus. Sometimes, one
could create subsets of the larger whole and put them together.
Reading the typing rules for F-pop is fine but (re)constructing them was the
more interesting exercise. Sort of like taking a
few easy pieces of System-F and then adding other required pieces that fit
just right to come up with whole (and sound)
System-F-pop.
To start with, the types are:
τ::=α∣τ1→τ2∣∀α.τ
the kinds are:
κ::=⋆∣∘
and the context is:
Γ::=⋅∣{(τ:⋆),(τ:∘),(x:τ⋆),(x:τ∘)}
The two kinds are related by the Kind-Sub rule:
Γ⊢τ:∘Γ⊢τ:⋆
In a context, a type τ
with kind ⋆
can be used as the same type
∘
, never the opposite.
Abstractions over terms (variables) are functions of the following type
where κ1
, κ2
both can be ⋆
or ∘
:
τ1:κ1→τ2:κ2
In System-F-pop, every type has a kind including function types which can be
marked with either kind as:
τ1:κ1⋆τ2:κ2
denotes a function that can be called any number of times, while
τ1:κ1∘τ2:κ2
denotes one which can be called only once, irrespective of what kinds both types τ1
, τ2
are.
Let’s take the T-Abs rule and start adding kinds to it.
T-Abs-1
Γ⊢λ⋆x:τ1.e:τ1⋆τ2Γ,x:τ1⋆⊢e:τ2κ2κ2:=⋆∣∘
T-Abs-2
Γ⊢λ∘x:τ1.e:τ1∘τ2Γ,x:τ1⋆⊢e:τ2κ2κ2:=⋆∣∘
T-Abs-3
Γ⊢λ∘x:τ1.e:τ1∘τ2Γ,x:τ1∘⊢e:τ2κ2κ2:=⋆∣∘
T-Abs-4
Γ⊢λ⋆x:τ1.e:τ1⋆τ2Γ,x:τ1∘⊢e:τ2κ2κ2:=⋆∣∘
In each of the four instances for the T-Abs rule, the expression abstracted over
can be linear or unrestricted. If the bound variable is linear (instances 3 and
4), it will only be used once within the lambda term. But the most important
thing to note is that the kind of the function type is independent of that of the
bound variable.
Do note that the κ3
over the
symbol for λ
is the same κ3
for the function type and denotes that the whole lambda
term of function type τ1→τ2
has kind κ3
.
Now if kinds κ1
and κ2
are completely independent of kind
κ3
, or in other words have no bearing on the kind determined for
κ3
, then what other factor does?
The answer is something outside of the lambda term itself. This makes the lambda
a closure that captures some value that is (obviously) not x
and not bound
in
e
. If a term or a value captured from outside λ
is linear, then the
closure formed by the lambda term capturing such a value is also linear. This
means that
having a captured linear value makes the lambda term be used only once without any
consideration of what the kinds for the bound variable and expression abstracted
are. If a term or a value captured from the surrounding scope is unrestricted, then
the closure can either be linear or unrestricted.
Up untill now, the context has been partioned as:
Γ::=⋅∣{(τ:⋆),(τ:∘),(x:τ⋆),(x:τ∘)}
The type system needs to make a simple determination of whether something
captured is linear or not. Maintaing four subsets in a larger set is more
cumbersome that two different sets, a practice also present in other type
systems. Moving the subset of linear variables out creates a new context:
Δ::=⋅∣Δ,x:τ∘
which only contains expressions (variables) that are of linear kind.
The original context now is:
Γ::=⋅∣{(τ:⋆),(τ:∘),(x:τ⋆)}
T-Abs (linear variable captured)
With two seperate contexts, Γ
and Δ
and a variable captured from
Δ
, the rule now adds a side condition:
Γ⊢λ∘x:τ1.e:τ1∘τ2Γ,x:τ1κ1⊢e:τ2κ2κ1,κ2:=⋆∣∘e captures at least one variable from Δ
This is a neater rule to come up with and it says that either the context
captures something linear (κ3=∘
) forcing the closure to be linear or the context cannot capture
anything linear (Δ
is empty) leaving the choice of the function kind to
either linear or unrestricted.
Having constucted two
contexts, the rule needs to account type-checking under both contexts in order
to satisfy the linear-value-captured requirement. For now, this will have to be
put aside because we haven’t fully fleshed out the typing rules for both the
contexts including ones where expressions (variables) are added to them. Let’s first finish the side conditions for T-Abs.
The left half of the disjunction specified when something linear captured
and κ3 is required to be linear as a result. The right half spelss out when
nothing linear can be captured and κ3
is free to be either linear or
restricted when it fits as a piece into a larger program that’s type-checked.
In System-F, the rule for term applications is:
Γ⊢e1e2:τ2κ3Γ⊢e1:τ1κ1τ2Γ⊢e2:τ1κ2κ1,κ2,κ3:=⋆∣∘
This rule is the obverse of T-Abs in that κ1
here is the kind of a
lambda term. Since κ1
was determined by capturing of values by the lambda
term, κ2
need not be the same as κ1
at all. For the rule to
apply, the types τ1
in e1
and e2
should match. The kind of the
result of the application, again, can be either i.e. whichever value for kind
satisfies all other typing constraints in the program.
T-App (both contexts in type checking?)
We didn’t have enough pieces to complete the T-Abs rule but let’s try and see
if the T-App rule can be completety type checked using both contexts. Starting
with the first fragment of the T-App rule:
...Γ;Δ⊢e1:τ1κ1τ2...
then:
...Γ;Δ⊢e1:τ1κ1τ2Γ;Δ⊢e2:τ1κ2
to give:
Γ;Δ⊢e1e2:τ2κ3Γ;Δ⊢e1:τ1κ1τ2Γ;Δ⊢e2:τ1κ2
But the above isn’t quite right. Because the linear contexts being the same for
all terms in the premises and the conclusions implies that all the terms are
type checked for the same set of linear variables. A variable captured
within e1
will be eventually used within e1
exclusively. If e2
has the
same context Δ
, the same variable has to be potentially allowed exclusive use within
e2
- something that would allow a linear variable to be used twice. To avoid
this mistake, the premises must have disjoint linear contexts:
The linear context Δ3
created by the disjoint union of the contexts
Δ1
, Δ2
is the one in which, along with Γ
, that the
application of e2
to e1
is type checked.
So far, we have an (incomplete) T-Abs rule, a (complete) T-App rule. But
there is something in the T-App rule that we haven’t fully worked out.
The linear context:
Δ::=⋅∣Δ,x:τ∘
stores variables that can only be used once. So the definition must also include
a base case where x
is a variable and:
Δ→Δ,x:τ∘x∈/Δ
This is to a single linear context. But in the T-App rule, the side condition
specifies the disjoint union of two linear contexts. How do we define that
operation? The base/empty case for both is the simplest:
⋅⊎⋅=⋅
With two sides to the union, an element can either to the left set:
Δ1,x:τ∘⊎Δ2=Δ,x:τ∘Δ1⊎Δ2=Δx∈/Δ
or the right one:
Δ1⊎Δ2,x:τ∘=Δ,x:τ∘Δ1⊎Δ2=Δx∈/Δ
and the resulting context is Δ
to which a linear variable not already
present in Δ
is added.
specifies the types and kinds to variables in the context with unrestricted
kinds. Writing out what κ
is in this context:
Γ⊢xκ:τx:τκ∈Γκ:=⋆
This rule is also incomplete like T-Abs as the linear context Δ
has not
been included in the type checking. I couldn’t think of what shape to put this
rule in, so I put it in the same incomplete slot as T-Abs until I could come
back to complete two (or more) rules.
The kinds κ1
and κ2
are independent of each other and the
type and kind of the expression never changes. The abstraction over typeα
allows the type system to plug in any type of kind κ1
for
checking e
.
Types and their kinds reside in the context Γ
, so adding the linear
context to the rule:
Γ;Δ⊢Λα:κ1.e:∀α:κ1.τΓ,α:κ1;Δ⊢e:τκ2α∈/Γ
does not change the linear context at all. With Γ
extended by adding
α
, α
gets added to the subset of types in it without capturing or
affecting any variables in e
.
Here with application of a type to another, the kinds for both being the same is
crucial. Both types have the same shape e.g. a Nat and a Bool have kind
⋆
.
Adding Δ
to the context to try and complete the rule:
Γ;Δ⊢e[τ]:{α↦τ}τ′Γ;Δ⊢e:∀α:κ1.τ′Γ⊢τ:κ2κ1=κ2
The same context Γ
has all the types, linear and unrestricted, as
separate subsets within it whereas Δ
(linear terms) are not used at all.
So using the same context in the premises and conclusion of the rule is correct.
These three above rules lay out the kind for each for arrow (function) types,
variables in Γ
and for quantified types. The Kind-Arr rule reiterates
that the kinds for the types within an arrow type, τ1
and τ2
, are
independent of the type of the arrow/function itself.
Writing all the rules in a table is me trying to put together smaller pieces to
form the larger picture.
The T-App, the Delta rules, T-TAbs, T-TApp and the rules for kinding
types are as complete as the kinds and contexts allow The T-Abs and the
T-Var rules are the one which type check kinds of linear or unrestricted types
but do not take the linear context (Delta) into consideration. These rules
need to be rewritten to complete the puzzle.
Let’s start with trying with the simple requirement: The T-Abs rule needs
Δ
along with Γ
to correctly type check. When a variable is added
as a binding in the premise of the rule, which context should it be put into?
VarAdd-Lin
The answer when the variable is linear is the linear context. The rule that
describes this is:
[Γ;Δ],x:τ⇝Γ;(Δ,x:τ)Γ⊢τ:∘x∈/Γ,Δ(x fresh)
Here Δ
and Γ
form two discrete subsets making up the entire
context [Γ;Δ]
to which x
is added. The ⇝
denotes
the variable being correctly put into the linear context.
VarAdd-Un
This rule is for when the fresh variable added to [Γ;Δ]
is
unrestricted and put into Γ
:
[Γ;Δ],x:τ⇝(Γ,x:τ);ΔΓ⊢τ:⋆x∈/Γ,Δ(x fresh)
Now T-Abs can be rewritten.
T-Abs (step 1 to update first premise only)
…[Γ;Δ],x:τ1⇝Γ′;Δ′Γ′;Δ′⊢e:τ2κ2…
The VarAdd-Lin/VarAdd-Un rules are used to put x
into the correct context
to create a new context [Γ′;Δ′]
and the expression e now typed
with this updated context instead of plain
[Γ;Δ]
T-Abs (step 2 to update second premise only)
……κ2:=⋆∣∘…
With VarAdd-Lin/VarAdd-Un having used the kind associated with type τ1
for x
, the side rule can be simplified to only keep κ2
.
T-Abs (step 3 to update third premise only)
……(Δ=⋅∧κ3=∘)∨(Δ=⋅∧κ3:=⋆∣∘)
The other side condition listing out the disjunctive requirement for kinds uses
the original Δ
. Should this be changed or not?
Δ
denotes the context that contains linear variables that can be captured
within e
. Δ′
is Δ
with x
added. Since x
and captured
values are never the same, letting the rule keep Δ
is correct and
sufficient.
T-Abs (step 4 to update the conclusion only)
Γ;Δ⊢λκ3x:τ1.e:τ1κ3τ2…
The conclusion is now typed under both Γ
and Δ
. Again should
Δ
be updated to Δ′
?
Here entire lambda expression λx:τ1.e
is a ready construct
within which both x
and e
are already type checked. So both
[Γ′;Δ′]
having already been used to type check e
, the lambda
construct can be type checked with [Γ;Δ]
alone.
T-Abs completed
Putting together the last four rewritten parts of the T-Abs rule gives a
satisfyingly complete rule in System-F∘
:
The three new/updated rules,
VarAdd-Lin, VarAdd-Un and T-Abs make System-F-pop’s type system closer
to being correct and complete. But when you look over all the rules, there’s one
that involves kinds of both types without involving the two contextsΔ
and Γ
.
That rule is T-Var where x is type checked to type τ
and either kind,
linear or unrestricted. But notice that the linear context Δ
is missing
from the rule.
T-UnrestrictVar
When the kind of x
is ⋆
, x
can only be a member of the unrestricted
context Γ
:
Γ;Δ⊢x:τ⋆x:τ⋆∈ΓΔ=?
I had trouble thinking of what Δ
would look like. The clue came from the
Delta-Empty, Delta-Left, Delta-Right rules.
A Δ
context can be constructed from
disjoint subsets. If a variable of kind ⋆
is in Γ
, there is no
linear set to construct or put this variable into. So Δ
can be simply
empty here as:
Γ;Δ⊢x:τ⋆x:τ⋆∈ΓΔ=⋅
The obverse happens when the variable is linear.
T-LinVar
Now the variable with linear kind ∘
can be put into the smallest set
Δx
. The typing rule is simply:
Γ;Δx⊢x:τ∘x:τ∘=Δx
When several linear variables (terms) are to be put together into one linear
context, say y
, z
, …, the disjoint union of all the subsets, Δy
,
Δz
, … make up Δ
.
Given the initial set of kinds for functions, contexts and types, most of the
rules inevitably end up being exactly what the paper describes. The real payoff
is in having derived some rules from others and writing out rules such as T-Abs in more detail
than provided in the paper, so that all side-conditions for that rule are
obvious.
Constructed rules vs. the paper’s (Figure 3), side by side #