Partial evaluation and Futamura projections #
Honest formulation (Jones/Gomard/Sestoft): the three projections follow from the mix
equation once the specializer is reflected as an object-level term (specTerm) with
selfApp. Mix alone admits a trivial specializer; nontriviality is a separate obligation.
Substitution function replacing variables with terms.
Equations
- ISAR.subst_env (ISAR.ITerm.var n) env = env n
- ISAR.subst_env ISAR.ITerm.norm env = ISAR.ITerm.norm
- ISAR.subst_env ISAR.ITerm.konst env = ISAR.ITerm.konst
- ISAR.subst_env ISAR.ITerm.dup env = ISAR.ITerm.dup
- ISAR.subst_env ISAR.ITerm.swap env = ISAR.ITerm.swap
- ISAR.subst_env ISAR.ITerm.comp env = ISAR.ITerm.comp
- ISAR.subst_env ISAR.ITerm.sₛ env = ISAR.ITerm.sₛ
- ISAR.subst_env (f · x) env = ISAR.subst_env f env · ISAR.subst_env x env
Instances For
Meta-level specializer (partial evaluator) replacing static variables.
Equations
- ISAR.specialize (ISAR.ITerm.var n) static_env = match static_env n with | some val => val | none => ISAR.ITerm.var n
- ISAR.specialize ISAR.ITerm.norm static_env = ISAR.ITerm.norm
- ISAR.specialize ISAR.ITerm.konst static_env = ISAR.ITerm.konst
- ISAR.specialize ISAR.ITerm.dup static_env = ISAR.ITerm.dup
- ISAR.specialize ISAR.ITerm.swap static_env = ISAR.ITerm.swap
- ISAR.specialize ISAR.ITerm.comp static_env = ISAR.ITerm.comp
- ISAR.specialize ISAR.ITerm.sₛ static_env = ISAR.ITerm.sₛ
- ISAR.specialize (f · x) static_env = ISAR.specialize f static_env · ISAR.specialize x static_env
Instances For
Setup for object-level Futamura projections.
spec is the meta specializer; specTerm is its reflection as an ITerm;
mix is the characterizing equation; selfApp says evaluating specTerm implements spec.
TrivialPE is a sorry-free non-optimizing instance (¬ Nontrivial, ¬ JonesOptimal).
OptimizingPE is a sorry-free fragment specializer with proved Nontrivial
(identity / konstβ folds + tagged residual fallback). JGS_PE is online PE for
the whole IStep signature (Nontrivial, ¬ JonesOptimal). Jones-optimality is
the cost criterion (JonesOptimal); a toy self-interpreter pair JonesIdPE
meets it. Full Jones–Gomard–Sestoft 1993 polyvariant BTA / cogen remains open;
specTerm is still swap, not an encoding of jgs_spec / offline_spec.
The projections below are the mix instantiations.
- specTerm : ITerm
Instances For
Cost measure for nontriviality (term size).
Equations
Instances For
Nontriviality: specialization strictly reduces cost on some nonempty class of programs.
Without this, mix+selfApp alone are satisfied by residualizers that do no optimization
(e.g. the identity residualizer spec p s = p).
Equations
- ISAR.Nontrivial S = ∃ (p : ISAR.ITerm), ∃ (s : ISAR.ITerm), ISAR.pe_cost (S.spec p s) < ISAR.pe_cost p
Instances For
Self-interpreter: running int on (src, d) agrees with running src on d.
Equations
- ISAR.SelfInterpreter S int = ∀ (src d : ISAR.ITerm), S.eval int (ISAR.pair src d) = S.eval src d
Instances For
Jones-optimality (Neil Jones): some self-interpreter specializes to a residual
no more expensive than the source, for every source. Cost/pe_cost form matches
Nontrivial. This is not 1993 polyvariant mix / compiler-generator quality;
specTerm is not an encoding of jgs_spec or offline_spec.
Equations
- ISAR.JonesOptimal S = ∃ (int : ISAR.ITerm), ISAR.SelfInterpreter S int ∧ ∀ (src : ISAR.ITerm), ISAR.pe_cost (S.spec int src) ≤ ISAR.pe_cost src
Instances For
Principled trivial PESetup (tagged residual, recursive unpack) #
Identity spec cannot satisfy selfApp for a universal eval prog _ := some prog.
Instead residualize under a dup tag and reflect the specializer as bare swap.
Evaluation recursively unpacks residuals so mix holds for all p (including when
p is itself tagged); selfApp is the swap clause. Cost never shrinks, so
¬ Nontrivial. A real self-applicable optimizing specTerm / BTA remains future work.
Tagged residual: dup · (pair p s). Strictly larger than p under pe_cost.
Equations
- ISAR.trivial_spec p s = ISAR.ITerm.dup · ISAR.pair p s
Instances For
Object-level evaluator for the toy specializer.
swaponpair p sreturns the tagged residual (selfApp).- A tagged residual applied to dynamic
dcontinues asrun p (pair s d)(mix), recursively, so residual-shaped programs do not break the mix equation. - Otherwise return the syntactic application.
Equations
- ISAR.trivial_run ISAR.ITerm.swap (ISAR.ITerm.konst · p · s) = ISAR.trivial_spec p s
- ISAR.trivial_run ISAR.ITerm.swap x✝ = ISAR.ITerm.swap · x✝
- ISAR.trivial_run (ISAR.ITerm.dup · (ISAR.ITerm.konst · p · s)) x✝ = ISAR.trivial_run p (ISAR.pair s x✝)
- ISAR.trivial_run x✝¹ x✝ = x✝¹ · x✝
Instances For
Equations
- ISAR.trivial_eval prog data = some (ISAR.trivial_run prog data)
Instances For
Toy PESetup: mix and selfApp by rfl; residualizer is cost-non-shrinking.
Equations
- ISAR.TrivialPE = { eval := ISAR.trivial_eval, spec := ISAR.trivial_spec, specTerm := ISAR.ITerm.swap, mix := ISAR.trivial_mix, selfApp := ISAR.trivial_selfApp }
Instances For
Mix+selfApp alone do not imply optimization: TrivialPE is not Nontrivial.
trivial_run returns konst · norm only by the default clause on those atoms.
Tagged residuals are strictly larger than the source, so no self-interpreter
can meet the Jones cost bound either — TrivialPE is not Jones-optimal.
Toy Jones-optimal pair (identity interpreter, not 1993 mix) #
norm as a degenerate self-interpreter: running it on (src, d) continues as
src on d. Specializing that interpreter copies the source (spec norm src = src),
so the Jones cost bound holds with equality. Mix/selfApp still use tagged residuals
for every other program. This is a cost-criterion toy, not a 1993 compiler-generator.
Equations
- ISAR.jones_spec p s = if p = ISAR.ITerm.norm then s else ISAR.trivial_spec p s
Instances For
Equations
- ISAR.jones_run ISAR.ITerm.swap (ISAR.ITerm.konst · p · s) = ISAR.jones_spec p s
- ISAR.jones_run ISAR.ITerm.swap x✝ = ISAR.ITerm.swap · x✝
- ISAR.jones_run (ISAR.ITerm.dup · (ISAR.ITerm.konst · p · s)) x✝ = ISAR.jones_run p (ISAR.pair s x✝)
- ISAR.jones_run ISAR.ITerm.norm (ISAR.ITerm.konst · p · s) = ISAR.jones_run p s
- ISAR.jones_run ISAR.ITerm.norm x✝ = ISAR.ITerm.norm · x✝
- ISAR.jones_run x✝¹ x✝ = x✝¹ · x✝
Instances For
Equations
- ISAR.jones_eval prog data = some (ISAR.jones_run prog data)
Instances For
Equations
- ISAR.JonesIdPE = { eval := ISAR.jones_eval, spec := ISAR.jones_spec, specTerm := ISAR.ITerm.swap, mix := ISAR.jones_mix, selfApp := ISAR.jones_selfApp }
Instances For
Optimizing fragment PE (identity / konstβ folding) #
Not a full Jones–Gomard–Sestoft BTA for all of ISAR. This is a principled optimizing specializer on a fragment:
- Peel
norm · body(identity elimination) and(konst · x) · y(konstβ / dead elim). - Atomic combinators (except
swap, reserved asspecTerm) residualize to themselves. - Remaining programs get a
dup-tagged residual (same packaging asTrivialPE).
mix / selfApp hold by computation on this evaluator; Nontrivial is witnessed by
stripping a norm redex. Full self-applicable optimizing mix for the whole calculus
remains dissertation-scale future work.
Meta specializer with static identity / konstβ folds.
Equations
- ISAR.opt_spec (ISAR.ITerm.norm · body) s = ISAR.opt_spec body s
- ISAR.opt_spec (ISAR.ITerm.konst · x · _y) s = ISAR.opt_spec x s
- ISAR.opt_spec ISAR.ITerm.norm s = ISAR.ITerm.norm
- ISAR.opt_spec ISAR.ITerm.konst s = ISAR.ITerm.konst
- ISAR.opt_spec ISAR.ITerm.sₛ s = ISAR.ITerm.sₛ
- ISAR.opt_spec ISAR.ITerm.dup s = ISAR.ITerm.dup
- ISAR.opt_spec ISAR.ITerm.comp s = ISAR.ITerm.comp
- ISAR.opt_spec (ISAR.ITerm.var n) s = ISAR.ITerm.var n
- ISAR.opt_spec p s = ISAR.ITerm.dup · ISAR.pair p s
Instances For
Object-level evaluator matching opt_spec:
swaponpair p simplementsselfApp.- Tagged residuals unpack via mix.
- Program-position
norm/konstβpeels mirroropt_spec. - Atomic values are data-insensitive (so constant residuals satisfy mix).
Equations
- ISAR.opt_run ISAR.ITerm.swap (ISAR.ITerm.konst · p · s) = ISAR.opt_spec p s
- ISAR.opt_run ISAR.ITerm.swap x✝ = ISAR.ITerm.swap · x✝
- ISAR.opt_run (ISAR.ITerm.dup · (ISAR.ITerm.konst · p · s)) x✝ = ISAR.opt_run p (ISAR.pair s x✝)
- ISAR.opt_run (ISAR.ITerm.norm · body) x✝ = ISAR.opt_run body x✝
- ISAR.opt_run (ISAR.ITerm.konst · x_2 · _y) x✝ = ISAR.opt_run x_2 x✝
- ISAR.opt_run ISAR.ITerm.norm x✝ = ISAR.ITerm.norm
- ISAR.opt_run ISAR.ITerm.konst x✝ = ISAR.ITerm.konst
- ISAR.opt_run ISAR.ITerm.sₛ x✝ = ISAR.ITerm.sₛ
- ISAR.opt_run ISAR.ITerm.dup x✝ = ISAR.ITerm.dup
- ISAR.opt_run ISAR.ITerm.comp x✝ = ISAR.ITerm.comp
- ISAR.opt_run (ISAR.ITerm.var n) x✝ = ISAR.ITerm.var n
- ISAR.opt_run x✝¹ x✝ = x✝¹ · x✝
Instances For
Equations
- ISAR.opt_eval prog data = some (ISAR.opt_run prog data)
Instances For
Optimizing fragment PESetup: mix/selfApp by induction/rfl; Nontrivial below.
Equations
- ISAR.OptimizingPE = { eval := ISAR.opt_eval, spec := ISAR.opt_spec, specTerm := ISAR.ITerm.swap, mix := ISAR.opt_mix, selfApp := ISAR.opt_selfApp }
Instances For
Witness program: norm · konst strips to konst.
Equations
Instances For
Jones–Gomard–Sestoft online PE for the whole IStep signature #
Offline BTA classifies subterms; online PE is the 1985 mix technique once every
object-language redex can fire at specialization time. ISAR's object reductions
are exactly normβ, konstβ, compβ, sβ. This specializer folds all four.
- Size-decreasing folds (
norm,konst,comp) recurse. sβcan growterm_size; we unfold once into a tagged residual (no recursion).swapremainsspecTerm. Polyvariant offline mix that generates a compiler-generator of Jones–Gomard–Sestoft 1993 quality is still open; the object-language signature is covered.
Offline binding-time sketch: swap is the dynamic hole; other atoms are static;
application is static iff both sides are. Monovariant: no call strings, no
program-point splitting. Unused by jgs_spec (online PE). Lemmas below
relate bta to specialization; they do not give 1993 polyvariant mix.
- static : BindingTime
- dynamic : BindingTime
Instances For
Equations
- ISAR.instReprBindingTime = { reprPrec := ISAR.instReprBindingTime.repr }
Equations
- ISAR.instReprBindingTime.repr ISAR.BindingTime.static prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "ISAR.BindingTime.static")).group prec✝
- ISAR.instReprBindingTime.repr ISAR.BindingTime.dynamic prec✝ = Repr.addAppParen (Std.Format.nest (if prec✝ ≥ 1024 then 1 else 2) (Std.Format.text "ISAR.BindingTime.dynamic")).group prec✝
Instances For
Equations
- ISAR.bta ISAR.ITerm.swap = ISAR.BindingTime.dynamic
- ISAR.bta (f · x_1) = match ISAR.bta f, ISAR.bta x_1 with | ISAR.BindingTime.static, ISAR.BindingTime.static => ISAR.BindingTime.static | x, x_2 => ISAR.BindingTime.dynamic
- ISAR.bta x✝ = ISAR.BindingTime.static
Instances For
Equations
- ISAR.containsSwap ISAR.ITerm.swap = true
- ISAR.containsSwap (f · x_1) = (ISAR.containsSwap f || ISAR.containsSwap x_1)
- ISAR.containsSwap x✝ = false
Instances For
Online specializer covering every IStep constructor.
Equations
- ISAR.jgs_spec (ISAR.ITerm.norm · body) x✝ = ISAR.jgs_spec body x✝
- ISAR.jgs_spec (ISAR.ITerm.konst · x_2 · _y) x✝ = ISAR.jgs_spec x_2 x✝
- ISAR.jgs_spec (ISAR.ITerm.comp · f · g · x_2) x✝ = ISAR.jgs_spec (f · (g · x_2)) x✝
- ISAR.jgs_spec (ISAR.ITerm.sₛ · x_2 · y · z) x✝ = ISAR.ITerm.var 0 · ISAR.pair (x_2 · z · (y · z)) x✝
- ISAR.jgs_spec ISAR.ITerm.norm x✝ = ISAR.ITerm.norm
- ISAR.jgs_spec ISAR.ITerm.konst x✝ = ISAR.ITerm.konst
- ISAR.jgs_spec ISAR.ITerm.sₛ x✝ = ISAR.ITerm.sₛ
- ISAR.jgs_spec ISAR.ITerm.dup x✝ = ISAR.ITerm.dup
- ISAR.jgs_spec ISAR.ITerm.comp x✝ = ISAR.ITerm.comp
- ISAR.jgs_spec (ISAR.ITerm.var n) x✝ = ISAR.ITerm.var n
- ISAR.jgs_spec x✝¹ x✝ = ISAR.ITerm.dup · ISAR.pair x✝¹ x✝
Instances For
Monovariant offline specializer: fold a redex only when bta classifies the
whole redex as static. Static S unfolds once into a residual with no s tag
(no recursion — size may grow). Dynamic terms are tagged residuals.
This is still monovariant (one division). It is not 1993 polyvariant mix.
Equations
- One or more equations did not get rendered due to their size.
- ISAR.offline_spec ISAR.ITerm.norm x✝ = ISAR.ITerm.norm
- ISAR.offline_spec ISAR.ITerm.konst x✝ = ISAR.ITerm.konst
- ISAR.offline_spec ISAR.ITerm.sₛ x✝ = ISAR.ITerm.sₛ
- ISAR.offline_spec ISAR.ITerm.dup x✝ = ISAR.ITerm.dup
- ISAR.offline_spec ISAR.ITerm.comp x✝ = ISAR.ITerm.comp
- ISAR.offline_spec (ISAR.ITerm.var n) x✝ = ISAR.ITerm.var n
- ISAR.offline_spec x✝¹ x✝ = if ISAR.bta x✝¹ = ISAR.BindingTime.static then x✝¹ else ISAR.ITerm.dup · ISAR.pair x✝¹ x✝
Instances For
Equations
- ISAR.jgs_run ISAR.ITerm.swap (ISAR.ITerm.konst · p · s) = ISAR.jgs_spec p s
- ISAR.jgs_run ISAR.ITerm.swap x✝ = ISAR.ITerm.swap · x✝
- ISAR.jgs_run (ISAR.ITerm.dup · (ISAR.ITerm.konst · p · s)) x✝ = ISAR.jgs_run p (ISAR.pair s x✝)
- ISAR.jgs_run (ISAR.ITerm.norm · body) x✝ = ISAR.jgs_run body x✝
- ISAR.jgs_run (ISAR.ITerm.konst · x_2 · _y) x✝ = ISAR.jgs_run x_2 x✝
- ISAR.jgs_run (ISAR.ITerm.comp · f · g · x_2) x✝ = ISAR.jgs_run (f · (g · x_2)) x✝
- ISAR.jgs_run (ISAR.ITerm.var 0 · (ISAR.ITerm.konst · c · s)) x✝ = c · ISAR.pair s x✝
- ISAR.jgs_run (ISAR.ITerm.sₛ · x_2 · y · z) x✝ = x_2 · z · (y · z) · x✝
- ISAR.jgs_run ISAR.ITerm.norm x✝ = ISAR.ITerm.norm
- ISAR.jgs_run ISAR.ITerm.konst x✝ = ISAR.ITerm.konst
- ISAR.jgs_run ISAR.ITerm.sₛ x✝ = ISAR.ITerm.sₛ
- ISAR.jgs_run ISAR.ITerm.dup x✝ = ISAR.ITerm.dup
- ISAR.jgs_run ISAR.ITerm.comp x✝ = ISAR.ITerm.comp
- ISAR.jgs_run (ISAR.ITerm.var n) x✝ = ISAR.ITerm.var n
- ISAR.jgs_run x✝¹ x✝ = x✝¹ · x✝
Instances For
Equations
- ISAR.jgs_eval prog data = some (ISAR.jgs_run prog data)
Instances For
Full-signature online PE: every IStep rule has a specialization clause.
Equations
- ISAR.JGS_PE = { eval := ISAR.jgs_eval, spec := ISAR.jgs_spec, specTerm := ISAR.ITerm.swap, mix := ISAR.jgs_mix, selfApp := ISAR.jgs_selfApp }
Instances For
Residual konst does not depend on the static argument. Used to rule out
swap as a self-interpreter (it specializes via jgs_spec).
If the first argument of pair is shared, jgs_run cannot return both
konst and dup (atoms ignore data; swap specializes independently of
the remaining static payload).
Online one-step JGS_PE has no self-interpreter: the three atom tests
(konst, dup, swap as sources) cannot hold together. Not Jones-optimal.
This is not a 1993 polyvariant mix / cogen claim.
Parameterized Futamura Square & Commutative Diagram #
Formal verification that all 4 execution pathways across the Futamura Projections commute to identical operational semantics:
Commutative Futamura Square Theorem: Evaluating the source program via direct interpretation (Pathway 0) produces the exact same result as executing the 1st Projection compiled target (Pathway 1).
Compiler Synthesis Commutative Theorem (2nd Projection): Applying the synthesized compiler to the source program yields the 1st projection target.
Compiler-Generator Synthesis Commutative Theorem (3rd Projection): Applying the synthesized cogen to an interpreter yields the 2nd projection compiler.
Full Commutative Futamura Square: All four pathways of the Futamura hierarchy compute identical values on all dynamic inputs.