Type-Indexed Rows
Mark Shields
Oregon Graduate Institute
Erik Meijer
Utrecht University
Motivation
• Investigate essence of closed-world type-based
overloading
– eg + is type-indexed product of Int Int Int and
Real Real Real functions
lightweight alternative to Haskell’s class system?
Motivation
• Investigate essence of closed-world type-based
overloading
– eg + is type-indexed product of Int Int Int and
Real Real Real functions
lightweight alternative to Haskell’s class system?
probably not!
Motivation
• Investigate essence of closed-world type-based
overloading
– eg + is type-indexed product of Int Int Int and
Real Real Real functions
lightweight alternative to Haskell’s class system?
probably not!
• Find compromise between XML types
– unions, “unordered” tuples
and functional programming types
– labeled variants and records
replace custom XML-centric languages with single
Haskell-like language?
Motivation
• Investigate essence of closed-world type-based
overloading
– eg + is type-indexed product of Int Int Int and
Real Real Real functions
lightweight alternative to Haskell’s class system?
probably not!
• Find compromise between XML types
– unions, “unordered” tuples
and functional programming types
– labeled variants and records
replace custom XML-centric languages with single
Haskell-like language? promising, but unimplemented
Overview
• Present lTIR, a typed polymorphic l-calculus
• Like a polymorphic extensible record calculus (lREC)
–
–
–
–
label-indexed rows
lacks/membership/insertion constraints
constrained/qualified polymorphism
records and variants
• But labels are not primitive
– type-indexed rows
– insertion and equality constraints
– type-indexed products (TIPs) and type-indexed co-products
(TICs)
– newtypes
lREC Combinators (1)
lREC Labels l
lREC Kinds Type Row
lREC Constraints l ins r
lREC Types
Empty
(l: _
One :
All :
: Row
# _) : Type Row Row
Row Type
Row Type
• eg
(xCoord: Int # yCoord: Real # Empty)
All (xCoord: Int # yCoord: Real # Empty)
One (isInt: Int # isBool: Bool # Empty)
lTIR Combinators (1)
lTIR Kinds Type, Row
lTIR Constraints t ins r, t eq u
lTIR Types
Empty
(
_
One :
All :
: Row
# _) : Type Row Row
Row Type
Row Type
• eg
(
All (
One (
Int #
Int #
Int #
Real # Empty)
Real # Empty)
Bool # Empty)
lREC Combinators (2)
lREC Terms
Triv : All Empty
(l: _ && _) : " a:Type b:Row . l ins b
a All b All (l: a # b)
(Inj l: _) : " a:Type b:Row . l ins b
a One (l: a # b)
• eg
(xCoord: 1 && yCoord: 2.0 && Triv) :
All (xCoord: Int # yCoord: Real # Empty)
(Inj isInt: 1) :
" a:Row . isInt ins a
One (isInt: Int # a)
lTIR Combinators (2)
lTIR Terms
Triv : All Empty
(
_ && _) : " a:Type b:Row . a ins b
a All b All (
a # b)
(Inj
_) : " a:Type b:Row . a ins b
a One (
a # b)
• eg
(
1 &&
2.0 && Triv) :
Real # Empty)
All (
Int #
(Inj
1) :
" a:Row .
Int ins a
One (
Int # a)
lREC Combinators (3)
lREC Patterns
Term combinators may also appear within l-abstraction
patterns
{ } allows discrimination - patterns tried left-to-right
• eg
\(xCoord: x && yCoord: y && Triv) .
x + floor y
· \(yCoord: y && _) . y * 2
· { \(Inj isInt: x) . x == 0
; \(Inj isBool: y) . not y } :
" a:Row . isInt ins a, isBool ins a
·
One (isInt: Int # isBool: Bool # a)Bool
lTIR Combinators (3)
lTIR Patterns
Term combinators may also appear within l-abstraction
patterns
{ } allows discrimination - patterns tried left-to-right
• eg
x &&
y && Triv) .
x + floor y
· \(
y && _) . y * 2
· { \(Inj
x) . x == 0
; \(Inj
y) . not y } :
" a:Row .
Int ins a,
Bool ins a
·
\(
One (
Int #
Bool # a)Bool
lREC Type Equality
Types equal up to permutation of rows
All (l1: t # l2: u # Empty)
All (l2: u # l1: t # Empty)
Records equal up to permutation of fields
(l1: t && l2: u && Triv)
(l2: u # l1: t && Triv)
lTIR Type Equality
Types equal up to permutation of rows
All (
t #
All (
u #
TIPs
(
u # Empty)
t # Empty)
equal up to permutation of fields
t &&
(
u #
u && Triv)
t && Triv)
!!!
Problem 1
lREC rows are label-indexed
Type
(xCoord: Int # yCoord: Int # Empty) : Row
• Term
(xCoord: 1 && yCoord: 2 && Triv) :
All (xCoord: Int # yCoord: Int # Empty)
Problem 1
lTIR rows are type-indexed
Type
(
Int #
Int # Empty) : Row
(well-kinded, but useless type)
• Term
(
1 &&
2 && Triv)
error: the constraint
Int ins (Int # Empty)
is unsatisfiable
• To be practical, lTIR cannot rely on structural
inequivalence alone to distinguish row elements
Solution: Newtypes
A newtype is a freshly named type synonym with
explicit rather than implicit isomorphism
eg with declarations
newtype
newtype
newtype
then Int,
A
B
C
A,
=
=
=
B
Int
\a . a
\a . a
Int, C Int are distinct types
eg
(1 && A 2 && B 3 && C 4 && Triv) :
All (Int # A # B Int # C Int # Empty)
Notice A
resembles a monomorphic label
B, C resemble
polymorphic labels
Problem 2
lTIR unification is not unitary
(\(x && y && Triv) . (x, y))
(1 && True && Triv)
Unification of
All (a # b # Empty)
with
All (Int # Bool # Empty)
yields the two substitutions
[a Int, b Bool], [a Bool, b Int]
Choosing one unifier destroys completeness
Maintaining all unifiers is dangerous
Solution: Equality Constraints
Obvious solution is to defer such unification problems
let f = (\(x && y && Triv) . (x, y))
(1 && True && Triv)
in fst f + snd f : Int
f : " a:Type b:Type .
(a # b # Empty) eq (Int # Bool # Empty),
a ins (b # Empty) (a, b)
Notice how equality constraint was eliminated when g
specialised to (Int, ...) and (..., Int)
Result is 2
Problem 3
lTIR is too polymorphic to simulate lREC
newtype A = \a .
newtype B = \a .
(A 1 && A True &&
All (A Int # A
a
a
B 1 && Triv) :
Bool # B Int # Empty)
But should be ill-typed
\r . ((\(A x && _) . x) (A 1 && r)) :
" a:Type b:Row c:Row .
(A a # b) eq (A Int # c),
A a ins b, A Int ins c All c a
But should be
" b:Row . A Int ins b All b Int
Solution: Opaque Newtypes
Opaque newtypes “hide” their type arguments within
insertion constraints
newtype opaque A = \a . a
newtype opaque B = \a . a
(A 1 && A True && B 1 && Triv) :
error: the constraint
A Int ins (A Bool # B Int # Empty)
is unsatisfiable
\r . ((\(A x && _) . x) (A 1 && r)) :
" b:Row . A Int ins b All b Int
or
" a:Type b:Row . A a ins b All b Int
Encoding Datatypes
Tuples are easily encoded using a newtype to labels
each posititon
Recursive sum-of-product datatypes also easily encoded
data Tree = \a . Node (Tree a, a, Tree a)
| Leaf
becomes
newtype Tree = \a . One ((Node a) #
(Leaf a) # Empty)
newtype Node = \a . (Tree a, a, Tree a)
newtype Leaf = \a . ()
Node t Tree (Inj (Node t))
Leaf
Tree (Inj (Leaf ()))
Encoding Closed World Overloading
intPlus : Int Int Int
realPlus : Real Real Real
(+) = (\(x && _) . x)
(intPlus && realPlus && Triv) :
"a:Type b:Row .
a ins b,
(a # b) eq ((Int Int Int) #
(Real Real Real) #
Empty) a
\y . (1 + 1, 1.0 + y) : Real (Int, Real)
Encoding Overloading (cont)
1.0 + 1
error: the constraint
((Real Int a)# b) eq
(Int Int Int) #
(Real Real Real) # Empty)
is unsatisfiable
XML
Document type definition (DTD) is a regular tree
grammar, document is a tree
Each production defines an element
Production rhs is a regular expression
eg (in idealised syntax!)
element Msg = (((To|Bcc)* & From), Body)
element Body = P*
element To = String
(...etc...)
But
Appendix E. Deterministic Content Models
For compatibility, it is required that content models
in element type declarations be deterministic.
Encoding XML DTDs
Sugar
(t | u) One (t # u # Empty)
(t & u) All (t # u # Empty)
t*
List t
t?
Option t
XML DTDs may be directly embedded within lTIR
(...etc...)
(...etc...)
elements
newtypes
regular expressions types
eg
newtype Msg = (((To|Bcc)* & From), Body)
newtype Body = P*
newtype To = String
(...etc...)
Encoding XML Documents
However, lTIR “native” syntax looks nothing like XML
Mgs (([Inj (To “Mark”), Inj (Bcc “Erik”)]
&& From “Erik” && Triv),
Body [P “1st para”, P “2nd para”])
But possible to exploit determinacy of regular
expressions to support native XML syntax - compiler
translates this into native syntax during type checking
<Msg><To>Mark</To><Bcc>Erik</Bcc>
<From>Erik</From>
<Body><P>1st para</P>
<P>2nd para</P></Body>
</Msg>
Semantics/Implementation
We may reduce type indexing to natural num indexing
Let be a partial ordering on types which is stable
under substitution, ie tutu
(We use a lexicographic ordering on the pre-order
flattening of t & u in canonical row order)
A TIP is a tuple in canonical row order, eg
[[ All (Bool # Int # Empty)]] =
{<v1, v2> v1 [[Int]], v2 [[Bool]]}^
A TIC is a pair of a canonical index and a value, eg
[[One (Bool # Int # Empty)]]=
{<1,v>v [[Int]]}^{<,v>v [[Bool]]}^
Semantics/Implementation (cont)
Since canonical order may not be known at compiletime, indices must be passed at run-time. Eg
\(x && y && Triv) . (x, y) :
"a:Type b:Type .
a ins (b # Empty)
All (a # b # Empty) (a, b)
is denoted/implemented by
lw . lp . let (x, p’) = remove w from p
in let (y, _) = remove 1 from p’
in <x, y>
Propagation of indices at run-time parallels the
propagation of insertion constraints at compile-time
Semantics/Implementation (cont)
Dictionary translation (Wadler & Blott) makes index
passing explicit
Constraint simplifier generates both absolute indices, eg
< w : Bool ins (Int # Char # Empty), C >
< C | w = 2>
and relative indices, eg
< w : Bool ins (Int # a # Empty), C >
< w’ : Bool ins (a # Empty) ,C | w = w’ + 1>
Correctness
Type checking builds upon constraint entailment:
Proof-theoretic entailment is incomplete w.r.t. model-theoretic
entailment
We currently do not exploit ordering properties of types, but
could do so should incompleteness prove problematic
Type soundness holds
Type inference builds upon constraint simplifier:
Does not reduce constraints to solved form (cf HM(X))
Soundness and completeness of inference holds
Completeness somewhat subtle, and must use model- rather
than proof-theoretic entailment
Practicality
lTIR is the kernel of XMl, a functional programming
language with direct support for XML types & terms
Haskell
OML
XMl
lTIR
Type inference is likely to have complexity above EXP
But simplifier is designed to eliminate common row
equality constraints in O(n log n) type comparisons (cf
polymorphic record calculi)
But at this time implementation is not complete enough
to test practicality on actual programs
Related Work
Like polymorphic extensible records... (Gaster et al)
Like set constraints... (Aiken et al)
… but without coherence
Almost instance of OML… (Jones)
… but with explicit idempotence constraints
Like intersection types... (Reynolds et al)
… but without labels
… but with finer instantiation ordering
Almost instance of HM(X)… (Odersky et al)
… but with coarser instantiation ordering
… and without normalisation requirement
Conclusions
Small calculus which naturally supports many
conventional type-theoretic features
recursive sum-of-products
extensible datatypes
monomorphic & polymorphic extensible records
type-based overloading
Also supports XML-style document type definitions
Complete constraint entailment seems overly expensive
Simplifier is quite weak, again to avoid blow ups
Implementation still in progress…
... so practicality still open
See Part I of my thesis the gory details (avail mid Feb)
© Copyright 2026 Paperzz