Implicit Syntax and Manual Override

First up, have a butcher’s at TypedLambda.epi. If you look hard enough, you’ll see that all sorts of implicit syntax is holding this thing together. Not only implicit argument synthesis, but implicit argument abstraction—the sort of thing which shows up with rank-n polymorphism. Getting this to behave at all sensibly was quite a big deal at the time and it’s still a bit garbled, to be honest. So let me try to make a clearer plan for version 2.

Epigram has implicit quantifiers, ∀_ and ∃_, which are distinct from their explicit counterparts. (In Lego, the implicit and explicit ∀ were identified, and this inevitably resulted in lots of little mishaps.) Epigram provides both implicit and explicit means to use and to construct elements of implicitly quantified types.

In the underlying type theory, everything is explicit. Use: the postfix _ inhibits implicit usage and instead converts its operand to an explicit function or pair.


\Rule%
  {\vt\hab Q\_\vx\hb\vS\TO\vT}%
  {\vt\_\hab Q\vx\hb\vS\TO\vT}
Q\in\{\forall,\exists\}

Construction: as you might expect, there’s a λ_; meanwhile, prefix _ overrides an implicit field in a tuple.


\Rule%
  {\vx\hb\vS \;\vdash\;\vt\hab\vT}%
  {\lambda\_\vx\hb\vS \TO \vt \hab \forall\_\vx\hb\vS \TO \vT}
\quad
\Rule%
  {\vs\hb\vS\quad (\vt)\hab\vT[\vs]}%
  {(\_\vs; \vt) \hab \exists\_\vx\hb\vS\TO\vT}

What about elaborating implicit usage and construction? Implicit usage, also known as completion is by argument synthesis for ∀_ and second projection for ∃_. Implicit construction is by implicit quantification for ∀_ and witness synthesis for ∃_. But how do these mechanisms kick in? We’d better take a closer look at how type information flows around in typechecking before we can figure that out. We can only perform implicit construction when we’re pushing type information downward; completion we can do anytime.

Let me do something like Pierce’s ‘local type inference’ thing, and have judgments representing types going down and coming up. Note that I use \vE to stand only for non-implicitly quantified types.


\[
\fbox{\mathtt{t}:\vT \;\downarrow\; \vt}
\quad
\fbox{\mathtt{t} \;\uparrow\; \vt:\vT}
\quad
\fbox{\vt:\vT \;\nearrow\; \vt\Pp:\vE}
\]

The first represents the situation where a type (possibly implicit) is being pushed into the elaboration of a concrete term. The second indicates that a (possibly implicit) type can be inferred for a concrete term. The third explains how this type may be completed to an explicit type. I write \mathtt{t}\nearrow\vt:\vE for the conjunction of \mathtt{t}\uparrow\vs:\vS and \vs:\vS\nearrow\vt:\vE, compressing inference-then-completion.

Here are the downward rules. I write \mathtt{m} for a term other than an explicit constructor for an implicit quantifier. If we’re pushing in an implicit quantifier, we adjust the problem accordingly. If we have an explicit constructor, we follow the underlying theory.


\Rule%
  {\purple{\bullet}:\vS\;\vdash\;\mathtt{m}:\vT[\purple{\bullet}]\;\downarrow\;\vt[\purple{\bullet}]}%
  {\mathtt{m}:\forall\_\vx\hb\vS\TO\vT\;\downarrow\;\lambda\_\vx\hb\vS\TO\vt[\vx]}
\quad
\Rule%
  {\orange{s}:\vS\quad\mathtt{m}:\vT[\orange{s}]\;\downarrow\;\vt}%
  {\mathtt{m}:\exists\_\vx\hb\vS\TO\vT \;\downarrow\; (\_\vs; \vt)}

The funny bullet is an anonymous nonce. You can’t make explicit reference to the implicitly bound variable. Fortunately, you can make implicit reference to it.


\Rule%
  {\vx:\vS\;\vdash\;\mathtt{t}:\vT\;\downarrow\;\vt}%
  {\lambda\_\vx\TO\mathtt{t}:\forall\_\vx\hb\vS\TO\vT\;\downarrow\;\lambda\_\vx\TO\vt}
\quad
\Rule%
  {\mathtt{s}:\vS\;\downarrow\;\vs\quad(\mathtt{t}):\vT[\vs]\;\downarrow\;\vt}%
  {(\_\mathtt{s}; \mathtt{t}):\exists\_\vx\hb\vS\TO\vT \;\downarrow\; (\_\vs; \vt)}

For explicit quantifiers and constructors, we now get


\Rule%
  {\vx:\vS \vdash \mathtt{t}:\vT\;\downarrow\;\vt}
  {\lambda\vx\TO\mathtt{t}:\forall\vx\hb\vS\TO\vT\;\downarrow\;\lambda\vx\hb\vS\TO\vt}
\quad
\Rule%
  {\mathtt{s}:\vS\;\downarrow\;\vs \quad (\mathtt{t}):\vT[\vs]\;\downarrow\;\vt}
  {(\mathtt{s}; \mathtt{t}):\exists\vx\hb\vS\TO\vT\;\downarrow\;(\vs; \vt)}

For terms other than abstraction and pairing in non-implicit types, we just infer and complete the type, then check it’s the one we want.


\Rule%
  {\mathtt{t}\;\nearrow\;\vt\Pp:\vE\Pp \quad \vE\Pp\le\vE}
  {\mathtt{t}:\vE\;\downarrow\;\vt\Pp}

Meanwhile, going the other way, the completion rules go like this:


\Axiom%
  {\vt:\vE\;\nearrow\;\vt:\vE}


\Rule%
  {\orange{s}:\vS\quad \vf\_\orange{s}:T[\orange{s}]\;\nearrow\;\vt:\vE}
  {\vf:\forall\_\vx\hb\vS\TO\vT\;\nearrow\;\vt:\vE}
\quad
\Rule%
  {\vp\_\FN{snd}:\vT[\vp\_\FN{fst}]\;\nearrow\;\vt:\vE}
  {\vp:\exists\_\vx\hb\vS\TO\vT\;\nearrow\;\vt:\vE}

And the upward rules (a selection thereof)…


\Axiom{\vx\;\uparrow\;\vx:\vS}\vx:\vS\in\Gamma


\Rule
  {\mathtt{f}\;\nearrow\;\vf:\forall\vx\hb\vS\TO\vT\quad \mathtt{s}:\vS\;\downarrow\;\vs}
  {\mathtt{f}\:\mathtt{s}\;\uparrow\;\vf\:\vs:\vT[\vs]}


\Rule
  {\mathtt{S}:\Type\;\downarrow\;\vS\quad \vx:\vS\vdash\mathtt{T}:\Type\;\downarrow\;\vT}
  {Q\vx\hb\mathtt{S}\TO\mathtt{T}\;\uparrow\;Q\vx\hb\vS\TO\vT:\Type}
  Q\in\{\forall,\exists,\forall\_,\exists\_\}


\Rule
  {\mathtt{t}\;\uparrow\;\vt:Q\_\vx\hb\vS\To\vT}
  {\mathtt{t}\_ \;\uparrow\; \vt\_:Q\vx\hb\vS\TO\vT}
Q\in\{\forall,\exists\}

Leave a Reply