Exercise 5.8 (Scott 1981, PRG-19, §5) — combinatory completeness #
(For combinator nuts.) Table 5.5 was meant to show how combinators could be defined in terms of
λ-expressions. Can the tables be turned to show that with enough combinators available, everyλ-expression can be defined by combining combinators, usingσ(τ)as the only mode of combination?
The answer is yes: this is the classical theorem of combinatory completeness (bracket abstraction). With the three combinators
I = λx.x, K = λx,y.x, S = λf,g,x. f(x)(g(x))
available, every λ-abstraction can be eliminated in favour of pure application
of combinators. We
formalise this in the neighbourhood-system framework as follows.
Dompackages a token type with its neighbourhood system, andDom.arrow A Bis the function domain(A → B)(its carrier isApproximableMap A.sys B.sys).I,K,Sare realised as concrete elements (Ielem,Kelem,Selem) of the appropriate function domains, with their value equationsIelem_apply,Kelem_apply,Selem_applyproved through the project'scurry/eval/projection API.Poly X Ais the intrinsically-typed syntax ofλ-bodies with one free variable of typeX: the variable itself, closed constants (any element — "enough combinators available"), and application. Its denotationPoly.denote t : |X| → |A|is the open term as a function of the variable, i.e. the body whose abstractionλx.twe wish to express.CL Ais the syntax of variable-free combinator expressions: constants and application only (σ(τ)is the only mode of combination). Its denotationCL.denote : |A|is a single element.bracket : Poly X A → CL (X.arrow A)is bracket abstraction: it turns an open bodytinto a closed combinator expression, using only application together with the constantsI,K,Sand the constants already occurring int— exactly Scott's challenge.
The completeness theorem is bracket_spec:
(bracket t).denote, applied to x, equals t.denote x for every x.
i.e. the variable-free combinator expression bracket t denotes precisely the
function λx.t. By
induction on t this is driven by the three combinator identities, turning the
table around as Scott
asks.
Everything is data; the combinators are built from idMap, curry, proj,
eval and are
choice-free.
A generic round-trip for the function-space representation. #
toApproxMap ∘ toFilter = id: the representation of a function-space
element by an
approximable
map is a left inverse of toFilter (this is funSpaceEquiv unfolded).
The S combinator as an approximable map. #
S = λf, g, x. f(x)(g(x)). We build the uncurried body
Sbody(⟨⟨F, G⟩, x⟩) = eval(eval(F, x), eval(G, x))
out of projections, pairing and eval, then curry twice.
The uncurried body of S: with F : 𝒟ₓ → (𝒟ₐ → 𝒟_b), G : 𝒟ₓ → 𝒟ₐ, x : 𝒟ₓ,
Sbody⟨⟨F, G⟩, x⟩ = (F x)(G x), expressed using only proj, pair and eval.
Equations
- One or more equations did not get rendered due to their size.
Instances For
S = λf, g, x. f(x)(g(x)), the curried form
(𝒟ₓ → (𝒟ₐ → 𝒟_b)) → ((𝒟ₓ → 𝒟ₐ) → (𝒟ₓ → 𝒟_b)).
Equations
Instances For
The defining equation of S: S(F)(G)(x) = F(x)(G(x)).
Domains as data, and the function-domain constructor. #
A domain: a token type together with its neighbourhood system. Bundling lets
us build an
intrinsically-typed syntax of combinator terms. We work over Type (universe
0), which covers all
the concrete domains of these lectures (N, T, C, …) and their
products/function spaces.
- carrier : Type
The token type.
- sys : NeighborhoodSystem self.carrier
The neighbourhood system on
carrier.
Instances For
The three combinators as elements of the appropriate function domains. #
S = λf, g, x. f(x)(g(x)) as an element of
((X → (A → B)) → ((X → A) → (X → B))).
Equations
Instances For
I(x) = x.
S(F)(G)(x) = F(x)(G(x)).
Syntax of λ-bodies with one free variable, and of variable-free #
combinator expressions.
Poly X A: an open term of type A with a single free variable of type X.
Constructors:
the variable, a closed constant (any element of any domain — "enough combinators
available"), and
application. This is the syntax of λ-bodies whose abstraction we want to
eliminate.
- var
{X : Dom}
: Poly X X
The free variable.
- con
{X A : Dom}
(c : A.sys.Element)
: Poly X A
A closed constant
c : |A|(in particular any available combinator). - app
{X A B : Dom}
(f : Poly X (A.arrow B))
(a : Poly X A)
: Poly X B
Application
f(a).
Instances For
CL A: a variable-free combinator expression of type A. The only
constructors are
constants and application, so application (σ(τ)) is the sole mode of
combination.
- con
{A : Dom}
(c : A.sys.Element)
: CL A
A closed constant.
- app
{A B : Dom}
(f : CL (A.arrow B))
(a : CL A)
: CL B
Application
f(a).
Instances For
The denotation of an open term: the body as a function of its free variable.
Equations
- Domain.Neighborhood.Exercise508.Poly.var.denote x✝ = x✝
- (Domain.Neighborhood.Exercise508.Poly.con c).denote x✝ = c
- (f.app a).denote x✝ = (Domain.Neighborhood.toApproxMap (f.denote x✝)).toElementMap (a.denote x✝)
Instances For
The denotation of a variable-free combinator expression: a single element.
Equations
Instances For
Bracket abstraction. #
Bracket abstraction. Turn an open body t (a λ-body with one free
variable) into a
variable-free combinator expression denoting λx.t, using only application and
the combinators
I, K, S together with the constants already occurring in t:
[x] x = I[x] c = K(c)(ca constant / closed term)[x] (f a) = S([x]f)([x]a).
Equations
- One or more equations did not get rendered due to their size.
- Domain.Neighborhood.Exercise508.bracket Domain.Neighborhood.Exercise508.Poly.var = Domain.Neighborhood.Exercise508.CL.con (Domain.Neighborhood.Exercise508.Ielem X)
Instances For
Combinatory completeness (Exercise 5.8, Scott 1981, PRG-19). The
variable-free combinator
expression bracket t denotes exactly the function λx.t: applied to any x, it
yields t.denote x.
Thus, with I, K, S (and the constants of t) available, every
λ-abstraction can be defined
by combining combinators using application as the only mode of combination —
Scott's "turning of the
tables".