That paper draft along with the supplementary material will have all the details, but I’ve decided that what I want to focus on in this post is all the other variations on the system we’ve tried that are either inconsistent or less expressive. This means I won’t cover a lot of motivation or examples (still a work in progress), or mention any metatheory unless where relevant; those can be found in the paper. Admittedly, these are mostly notes for myself, and I go at a pace that sort of assumes enough familiarity with the system to be able to verify well-typedness mentally, but this might be interesting to someone else too.
The purpose of StraTT is to introduce a different way of syntactically dealing with type universes, and the premise is to take a predicative type theory with a universe hierarchy, and instead of stratifying universes by levels, you instead stratify typing judgements themselves, a strategy inspired by Stratified System F.^{2} This means level annotations appear in the shape of the judgement $\Gamma \vdash a \mathbin{:}^{j} A$, where $a$ is a term, $A$ is a type, $j$ is a level, and $\Gamma$ is a list of declarations $x \mathbin{:}^{j} A$.
Here are the rules for functions.
$\frac{ \Gamma \vdash A \mathbin{:}^{j} \star \quad \Gamma, x \mathbin{:}^{j} A \vdash B \mathbin{:}^{k} \star \quad j < k }{ \Gamma \vdash \Pi x \mathbin{:}^{j} A \mathpunct{.} B \mathbin{:}^{k} \star } \quad \frac{ \Gamma, x \mathbin{:}^{j} A \vdash b \mathbin{:}^{k} B \quad j < k }{ \Gamma \vdash \lambda x \mathpunct{.} b \mathbin{:}^{k} \Pi x \mathbin{:}^{j} A \mathpunct{.} B } \quad \frac{ \Gamma \vdash b \mathbin{:}^{k} \Pi x \mathbin{:}^{j} A \mathpunct{.} B \quad \Gamma \vdash a \mathbin{:}^{j} A }{ \Gamma \vdash b \; a \mathbin{:}^{k} B[x \mapsto a] }$Function introduction and elimination are as expected, just with level annotations sprinkled in; the main difference is in the dependent function type. Just as the stratification of universes into a hierarchy serves to predicativize function types, so too does stratification of judgements, here done explicitly through the constraint $j < k$. This means that if you have a function of type $\Pi x \mathbin{:}^{j} \star \mathpunct{.} B$ at level $k$, you can’t pass that same type to the function as the type argument, since its level is too big.
Because universes no longer have levels, we do in fact have a type-in-type rule $\Gamma \vdash \star \mathbin{:}^{j} \star$ for any $j$. But this is okay!^{3} The judgement stratification prevents the kind of self-referential tricks that type-theoretic paradoxes typically take advantage of. The simplest such paradox is Hurkens’ paradox, which is still quite complicated, but fundamentally involves the following type.
$\mathsf{\textcolor{#bf616a}{U}} \mathbin{:} \star^{1} \coloneqq \Pi X \mathbin{:}^{0} \star \mathpunct{.} ((X \to \star) \to \star) \to X) \to ((X \to \star) \to \star)$For the paradox to work, the type argument of a function of type $\mathsf{\textcolor{#bf616a}{U}}$ needs to be instantiated with $\mathsf{\textcolor{#bf616a}{U}}$ itself, but stratification prevents us from doing that, since $1 \nleq 0$.
Cumulativity is both normal to want and possible to achieve. There are two possible variations to achieve it: one adds cumulativity to the variable rule and leaves conversion alone.
$\frac{ x \mathbin{:}^{j} A \in \Gamma \quad j \le k }{ \Gamma \vdash x \mathbin{:}^{k} A } \quad \frac{ \Gamma \vdash a \mathbin{:}^{k} A \quad A \equiv B }{ \Gamma \vdash a \mathbin{:}^{k} B }$Alternatively, the variable rule can be left alone, and cumulativity integrated into the conversion rule.
$\frac{ x \mathbin{:}^{j} A \in \Gamma }{ \Gamma \vdash x \mathbin{:}^{j} A } \quad \frac{ \Gamma \vdash a \mathbin{:}^{j} A \quad A \equiv B \quad j \leq k }{ \Gamma \vdash a \mathbin{:}^{k} B }$Either set is admissible in terms of the other. I’m not going to tell you which one I’ve picked.
Level annotations are tedious and bothersome. Can we omit them from function types? The answer is no. Doing so allows us to derive exactly the inconsistency we set out to avoid. Suppose our function type domains aren’t annotated with levels, and let $u \mathbin{:}^{1} \mathsf{\textcolor{#bf616a}{U}}$. Then by cumulativity, we can raise its level, then apply it to $\mathsf{\textcolor{#bf616a}{U}}$.
$\frac{ u \mathbin{:}^{1} \mathsf{\textcolor{#bf616a}{U}} \vdash u \mathbin{:}^{2} \Pi X \mathbin{:} \star \mathpunct{.} ((X \to \star) \to \star) \to X) \to ((X \to \star) \to \star) \quad \vdash \mathsf{\textcolor{#bf616a}{U}} \mathbin{:}^{1} \star }{ u \mathbin{:}^{1} \mathsf{\textcolor{#bf616a}{U}} \vdash u \; \mathsf{\textcolor{#bf616a}{U}} \mathbin{:}^{2} (((\mathsf{\textcolor{#bf616a}{U}} \to \star) \to \star) \to \mathsf{\textcolor{#bf616a}{U}}) \to ((\mathsf{\textcolor{#bf616a}{U}} \to \star) \to \star) }$The strict level constraint is still there, since the level of $\mathsf{\textcolor{#bf616a}{U}}$ remains strictly smaller than that of $u$. But without the annotation, the allowed level of the domain can rise as high as possible, yet still within the constraint, via cumulativity.
The formalism of StraTT also supports a global context $\Delta$ which consists of a list of global definitions $x \mathbin{:}^{j} A \coloneqq a$, where $x$ is a constant of type $A$ and body $a$ at level $j$. Separately from cumulativity, we have displacement, which is based on Conor McBride’s notion of “crude-but-effective stratification”:^{4} global definitions are defined with fixed, constant levels, and then uniformly incremented as needed. This provides a minimalist degree of code reusability across levels without actually having to introduce any sort of level polymorphism. Formally, we have the following rules for displaced constants and their reduction behaviour that integrates both displacement and cumulativity.
$\frac{ x \mathbin{:}^{j} A \coloneqq a \in \Delta \quad i + j \leq k }{ \Delta; \Gamma \vdash x^{i} \mathbin{:}^{k} A^{+i} } \quad \frac{ x \mathbin{:}^{j} A \coloneqq a \in \Delta }{ \Delta \vdash x^{i} \rightsquigarrow a^{+i} }$The metafunction ${\cdot}^{+i}$ recursively adds $i$ to all levels and displacements in a term; below are the two cases where the increment actually has an effect.
$\begin{align*} (\Pi x \mathbin{:}^{j} A. B)^{+i} &≝ \Pi x \mathbin{:}^{i + j} A^{+i}. B^{+i} \\ (x^{j})^{+i} &≝ x^{i + j} \end{align*}$As an example of where displacement is useful, we can define a function that takes an argument $u$ of the displaced type $\mathsf{\textcolor{#bf616a}{U}}^{1}$, which now takes a type argument at level $1$, so $u \; \mathsf{\textcolor{#bf616a}{U}}$ is then well typed.
You’ll notice that I’ve been writing nondependent functions with arrows but without any level annotations. This is because in StraTT, there’s a separate syntactic construct for nondependent functions which behaves just like functions but no longer has the strict stratification restriction.
$\frac{ \Gamma \vdash A \mathbin{:}^{k} \star \quad \Gamma \vdash B \mathbin{:}^{k} \star }{ \Gamma \vdash A \to B \mathbin{:}^{k} \star } \quad \frac{ \Gamma, x \mathbin{:}^{k} A \vdash b \mathbin{:}^{k} B }{ \Gamma \vdash \lambda x \mathpunct{.} b \mathbin{:}^{k} A \to B } \quad \frac{ \Gamma \vdash b \mathbin{:}^{k} A \to B \quad \Gamma \vdash a \mathbin{:}^{k} A }{ \Gamma \vdash b \; a \mathbin{:}^{k} B }$Removing the restriction is okay for nondependent function types, because there’s no risk of dependently instantiating a type variable in the type with the type itself.
These are called floating functions because rather than being fixed, the level of the domain of the function type “floats” with the level of overall function type (imagine a buoy bobbing along as the water rises and falls). Specifically, cumulativity lets us derive the following judgement.
$\frac{ f \mathbin{:}^{j} A \to B \vdash f \mathbin{:}^{j} A \to B \quad j \leq k }{ f \mathbin{:}^{j} A \to B \vdash f \mathbin{:}^{k} A \to B }$Unusually, if we start with a function $f$ at level $j$ that takes some $A$ at level $j$, we can use $f$ at level $k$ as if it now takes some $A$ at a higher level $k$. The floating function $f$ is covariant in the domain with respect to the levels! Typically functions are contravariant or invariant in their domains with respect to the ambient ordering on types, universes, levels, etc.^{5}
Designing StraTT means not just designing for consistency but for expressivity too: fundamentally revamping how we think about universes is hardly useful if we can’t also express the same things we could in the first place.
First, here’s an example of some Agda code involving self-applications of identity functions in ways such that universe levels are forced to go up to in a nontrivial manner (i.e. not just applying bigger and bigger universes to something). Although I use universe polymorphism to define $\mathsf{\textcolor{#bf616a}{ID}}$, it isn’t strictly necessary, since all of its uses are inlineable without issue.
{-# OPTIONS --cumulativity #-}
open import Level
ID : ∀ ℓ → Set (suc ℓ)
ID ℓ = (X : Set ℓ) → X → X
𝟘 = zero
𝟙 = suc zero
𝟚 = suc 𝟙
𝟛 = suc 𝟚
-- id id
idid1 : ID 𝟙 → ID 𝟘
idid1 id = id (ID 𝟘) (λ x → id x)
-- id (λid. id id) id
idid2 : ID 𝟚 → ID 𝟘
idid2 id = id (ID 𝟙 → ID 𝟘) idid1 (λ x → id x)
-- id (λid. id (λid. id id) id) id
idid3 : ID 𝟛 → ID 𝟘
idid3 id = id (ID 𝟚 → ID 𝟘) idid2 (λ x → id x)
In $\mathsf{\textcolor{#bf616a}{idid1}}$, to apply $\mathit{id}$ to itself, its type argument needs to be instantiated with the type of $\mathit{id}$, meaning that the level of the $\mathit{id}$ on the left needs to be one larger, and the $\mathit{id}$ on the right needs to be eta-expanded to fit the resulting type with the smaller level. Repeatedly applying self-applications increases the level by one each time. I don’t think anything else I can say will be useful; this is one of those things where you have to stare at the code until it type checks in your brain (or just dump it into the type checker).
In the name of expressivity, we would like these same definitions to be typeable in StraTT. Suppose we didn’t have floating functions, so every function type needs to be dependent. This is the first definition that we hope to type check.
$$\begin{array}{rl}{\displaystyle {\mathsf{idid1}}}& {\displaystyle {:}^{3}\mathrm{\Pi}id{:}^{2}(\mathrm{\Pi}X{:}^{1}\star \mathrm{.}\mathrm{\Pi}x{:}^{1}X\mathrm{.}X)\mathrm{.}(\mathrm{\Pi}X{:}^{0}\star \mathrm{.}\mathrm{\Pi}x{:}^{0}X\mathrm{.}X)}\\ & {\displaystyle \mathrm{\u2254}\lambda id\mathrm{.}id\text{\hspace{0.25em}\hspace{0.05em}}(\mathrm{\Pi}X{:}^{0}\star \mathrm{.}\mathrm{\Pi}x{:}^{0}X\mathrm{.}X)\text{\hspace{0.25em}\hspace{0.05em}}(\lambda X\text{\hspace{0.25em}\hspace{0.05em}\hspace{0.05em}}x\mathrm{.}\overline{)id\text{\hspace{0.25em}\hspace{0.05em}}X\text{\hspace{0.25em}\hspace{0.05em}}x})}\end{array}$$The problem is that while $\mathit{id}$ expects its second argument to be at level $1$, the actual second argument contains $\mathit{id}$ itself, which is at level $2$, so such a self-application could never fit! And by stratification, the level of the argument has to be strictly smaller than the level of the overall function, so there’s no annotation we could fiddle about with to make $\mathit{id}$ fit in itself.
What floating functions grant us is the ability to do away with stratification when we don’t need it. The level of a nondependent argument to a function can be as large as the level of the function itself, which is exactly what we need to type check this first definition that now uses floating functions.
$\begin{align*} \mathsf{\textcolor{#bf616a}{idid1}} &\mathbin{:}^{2} (\Pi X \mathbin{:}^{1} \star \mathpunct{.} X \to X) \to (\Pi X \mathbin{:}^{0} \star \mathpunct{.} X \to X) \\ &\coloneqq \lambda \mathit{id} \mathpunct{.} \mathit{id} \; (\Pi X \mathbin{:}^{0} \mathpunct{.} \star X \to X) \; (\lambda X \mathpunct{.} \mathit{id} \; X) \end{align*}$The corresponding definitions for $\mathsf{\textcolor{#bf616a}{idid2}}$ and $\mathsf{\textcolor{#bf616a}{idid3}}$ will successfully type check too; exercise for the reader.
I’ve made it sound like all nondependent functions can be made to float, but unfortunately this isn’t always true. If the function argument itself is used in a nondependent function domain, then that argument is forced to be fixed, too. Consider for example the following predicate that states that the given type is a mere proposition, i.e. that all of its inhabitants are indistinguishable.
$\begin{align*} \mathsf{\textcolor{#bf616a}{isProp}} &\mathbin{:}^1 \Pi X \mathbin{:}^0 \star \mathpunct{.} \star \\ &\coloneqq \lambda X \mathpunct{.} \Pi x \mathbin{:}^0 X \mathpunct{.} \Pi y \mathbin{:}^0 X \mathpunct{.} \Pi P \mathbin{:}^0 X \to \star \mathpunct{.} P \; x \to P \; y \end{align*}$If $\mathsf{\textcolor{#bf616a}{isProp}}$ were instead assigned the floating function type $\star \to \star$ at level $1$, the type argument $X$ being at level $1$ would force the level of $P$ to also be $1$, which would force the overall definition to be at level $2$, which would then make $X$ be at level $2$, and so on.
Not only are not all nondependent functions necessarily floating, not all functions that could be floating necessarily need to be nondependent, either. I’m unsure of how to verify this, but I don’t see why function types like the identity function type $(X \mathbin{:} \star) \to X \to X$ can’t float; I can’t imagine that being exploited to derive an inconsistency. The fixed/floating dichotomy appears to be independent of the dependent/nondependent dichotomy, and matching them up only an approximation.
Before I move on to the next design decision, I want to state and briefly discuss an important lemma used in our metatheory. We’ve proven type safety (that is, progress and preservation lemmas) for StraTT, and the proof relies on a restriction lemma.
Definition: Given a context $\Gamma$ and a level $j$, the restriction $\lceil\Gamma\rceil_{j}$ discards all declarations in $\Gamma$ of level strictly greater than $j$.
Lemma [Restriction]: If $\vdash \Gamma$ and $\Gamma \vdash a \mathbin{:}^{j} A$, then $\vdash \lceil\Gamma\rceil_{j}$ and $\lceil\Gamma\rceil_{j} \vdash a \mathbin{:}^{j} A$.
For the lemma to hold, no derivation of $\Gamma \vdash a \mathbin{:}^{j} A$ can have premises whose level is strictly greater than $j$; otherwise, restriction will discard those necessary pieces. This lemma is crucial in proving specifically the floating function case of preservation; if we didn’t have floating functions, the lemma isn’t necessary.
As it stands, StraTT enforces that the level of a term’s type is exactly the level of the term. In other words, the following regularity lemma is provable.
Lemma [Regularity]: If $\Gamma \vdash a \mathbin{:}^{j} A$, then $\Gamma \vdash A \mathbin{:}^{j} \star$.
But what if we relaxed this requirement?
Lemma [Regularity (relaxed)]: If $\Gamma \vdash a \mathbin{:}^{j} A$, then there is some $k \geq j$ such that $\Gamma \vdash A \mathbin{:}^{k} \star$.
In such a new relaxed StraTT (RaTT), the restriction lemma is immediately violated, since $\Gamma ≝ A \mathbin{:}^{1} \star, x \mathbin{:}^{0} A$ is a well-formed context, but $\lceil\Gamma\rceil_{0} = x \mathbin{:}^{0} A$ isn’t even well scoped. So a system with relaxed levels that actually makes use of the relaxation can no longer accommodate floating functions because type safety won’t hold.
However, the dependent functions of RaTT are more expressive: the $\mathit{id}$ self-application can be typed using dependent functions alone!^{6} First, let’s pin down the new rules for our functions.
$\frac{ \Gamma \vdash A \mathbin{:}^{k} \star \quad \Gamma, x \mathbin{:}^{j} A \vdash B \mathbin{:}^{k} \star \quad j < k }{ \Gamma \vdash \Pi x \mathbin{:}^{j} A \mathpunct{.} B \mathbin{:}^{k} \star } \quad \frac{ \Gamma, x \mathbin{:}^{j} A \vdash b \mathbin{:}^{k} B }{ \Gamma \vdash \lambda x \mathpunct{.} b \mathbin{:}^{k} \Pi x \mathbin{:}^{j} A \mathpunct{.} B } \quad \frac{ \Gamma \vdash b \mathbin{:}^{k} \Pi x \mathbin{:}^{j} A \mathpunct{.} B \quad \Gamma \vdash a \mathbin{:}^{j} A }{ \Gamma \vdash b \; a \mathbin{:}^{k} B[x \mapsto a] }$Two things have changed:
Since the premise $j < k$ still exists in the type formation rule, we can be assured that a term of type $\mathsf{\textcolor{#bf616a}{U}}$ still can’t be applied to $\mathsf{\textcolor{#bf616a}{U}}$ itself and that we still probably have consistency.^{7} Meanwhile, the absence of $j < k$ in the function introduction rule allows us to inhabit, for instance, the identity function type $\Pi X \mathbin{:}^{1} \star \mathpunct{.} \Pi x \mathbin{:}^{0} X \mathpunct{.} X$, where the level annotations no longer match.
These rules let us assign levels to the $\mathit{id}$ self-application as follows.
$\begin{align*} \mathsf{\textcolor{#bf616a}{idid1}} &\mathbin{:}^{0} \Pi \mathit{id} \mathbin{:}^{0} (\Pi X \mathbin{:}^{1} \star \mathpunct{.} \Pi x \mathbin{:}^{0} X \mathpunct{.} X) \mathpunct{.} (\Pi X \mathbin{:}^{0} \star \mathpunct{.} \Pi x \mathbin{:}^{0} X \mathpunct{.} X) \\ &\coloneqq \lambda \mathit{id} \mathpunct{.} \mathit{id} \; (\Pi X \mathbin{:}^{0} \star \mathpunct{.} \Pi x \mathbin{:}^{0} X \mathpunct{.} X) \; (\lambda X \; x \mathpunct{.} \mathit{id} \; X \; x) \end{align*}$Although the type of $\mathit{id}$ must live at level $2$, the $\mathit{id}$ term can be assigned a lower level $0$. This permits the self-application to go through, since the second argument of $\mathit{id}$ demands a term at level $0$. Unusually, despite the second $\mathit{id}$ being applied to a type at level $1$, the overall level of the second argument is still $0$ because quantification over arguments at higher levels is permitted.
Despite the apparently added expressivity of these new dependent functions, unfortunately they’re still not as expressive as actually having floating functions. This was something I discovered while trying to type check Hurkens’ paradox and found that fewer definitions were typeable. I’ve attempted to isolate the issue a bit by revealing a similar problem when working with CPS types. Let’s first look at some definitions in the original StraTT with floating functions.
$\begin{array}{r l l} \mathsf{\textcolor{#bf616a}{CPS}} &\mathbin{:}^{1} \star \to \star &\coloneqq \lambda A \mathpunct{.} \Pi X \mathbin{:}^{0} \star. (A \to X) \to X \\ \mathsf{\textcolor{#bf616a}{return}} &\mathbin{:}^{1} \Pi A \mathbin{:}^{0} \star \mathpunct{.} A \to \mathsf{\textcolor{#bf616a}{CPS}} \; A &\coloneqq \lambda A \; a \; X \; f. f \; a \\ \mathsf{\textcolor{#bf616a}{run}} &\mathbin{:}^{1} \Pi A \mathbin{:}^{0} \star \mathpunct{.} \mathsf{\textcolor{#bf616a}{CPS}} \; A \to A &\coloneqq \lambda A \; f \mathpunct{.} f \; A \; (\lambda a \mathpunct{.} a) \\ \mathsf{\textcolor{#bf616a}{idCPS}} &\mathbin{:}^{2} \Pi A \mathbin{:}^{0} \star \mathpunct{.} \mathsf{\textcolor{#bf616a}{CPS}}^{1} \; A \to \mathsf{\textcolor{#bf616a}{CPS}} \; A &\coloneqq \lambda A \; f. f \; (\mathsf{\textcolor{#bf616a}{CPS}} \; A) \; (\mathsf{\textcolor{#bf616a}{return}} \; A) \\ \mathsf{\textcolor{#bf616a}{runReturn}} &\mathbin{:}^{2} \Pi A \mathbin{:}^{0} \star \mathpunct{.} \mathsf{\textcolor{#bf616a}{CPS}}^{1} \; A \to \star &\coloneqq \lambda A \; f \mathpunct{.} \mathsf{\textcolor{#bf616a}{run}} \; A \; (\mathsf{\textcolor{#bf616a}{idCPS}} \; A \; f) = \mathsf{\textcolor{#bf616a}{run}}^{1} \; A \; f \end{array}$$\mathsf{\textcolor{#bf616a}{CPS}}$ translates a type to its answer-polymorphic CPS form, and $\mathsf{\textcolor{#bf616a}{return}}$ translates a term into CPS. $\mathsf{\textcolor{#bf616a}{run}}$ does the opposite and runs the computation with the identity continuation to yield a term of the original type. $\mathsf{\textcolor{#bf616a}{runReturn}}$ is a proposition we might want to prove about a given type and a computation of that type in CPS: passing $\mathsf{\textcolor{#bf616a}{return}}$ as the continuation for our computation (as in $\mathsf{\textcolor{#bf616a}{idCPS}}$) to get another computation should, when run, yield the exact same result as running the computation directly.
By careful inspection,^{8} everything type checks. Displacements needed are in $\mathsf{\textcolor{#bf616a}{idCPS}}$ and $\mathsf{\textcolor{#bf616a}{runReturn}}$ on the type of the computation $f$. This displacement raises the level of the answer type argument of $f$ so that the type can be instantiated with $\mathsf{\textcolor{#bf616a}{CPS}} \; A$. Consequently, we also need to displace the $\mathsf{\textcolor{#bf616a}{run}}^{1}$ that runs the displaced computation on the right-hand side of $\mathsf{\textcolor{#bf616a}{runReturn}}$. Meanwhile, left-hand $\mathsf{\textcolor{#bf616a}{run}}$ shouldn’t be displaced because it runs the undisplaced computation returned by $\mathsf{\textcolor{#bf616a}{idCPS}}$.
Now let’s try to do the same in the new RaTT without floating functions. I’ll only write down the definition of $\mathsf{\textcolor{#bf616a}{CPS}}$ and $\mathsf{\textcolor{#bf616a}{idCPS}}$, and inline $\mathsf{\textcolor{#bf616a}{return}}$ in the latter.
$$\begin{array}{rll}{\textstyle {\mathsf{CPS}}}& {\textstyle {:}^{1}\mathrm{\Pi}A{:}^{0}\star \mathrm{.}\star}& {\textstyle \mathrm{\u2254}\lambda A\mathrm{.}\mathrm{\Pi}X{:}^{0}\star \mathrm{.}\mathrm{\Pi}f{:}^{0}(\mathrm{\Pi}a{:}^{0}A\mathrm{.}X)\mathrm{.}X}\\ {\textstyle {\mathsf{idCPS}}}& {\textstyle {:}^{0}\mathrm{\Pi}A{:}^{0}\star \mathrm{.}\mathrm{\Pi}f{:}^{0}{{\mathsf{CPS}}}^{1}\text{\hspace{0.25em}\hspace{0.05em}}A\mathrm{.}{\mathsf{CPS}}\text{\hspace{0.25em}\hspace{0.05em}}A}& {\textstyle \mathrm{\u2254}\lambda A\text{\hspace{0.25em}\hspace{0.05em}}f\mathrm{.}f\text{\hspace{0.25em}\hspace{0.05em}}({\mathsf{CPS}}\text{\hspace{0.25em}\hspace{0.05em}}A)\text{\hspace{0.25em}\hspace{0.05em}}(\lambda a\text{\hspace{0.25em}\hspace{0.05em}}X\text{\hspace{0.25em}\hspace{0.05em}}g\mathrm{.}\overline{)g\text{\hspace{0.25em}\hspace{0.05em}}a})}\end{array}$$This is a little more difficult to decipher, so let’s look at what the types of the different pieces of $\mathsf{\textcolor{#bf616a}{idCPS}}$ are.
To fix this, we might try to increase the level annotation on $a$ in the definition of $\mathsf{\textcolor{#bf616a}{CPS}}$ so that $g$ can accommodate $a$. But doing so would also increase the level annotation on $a$ in the displaced type of $f$ to $2$, meaning that $a$ is still too large to fit into $g$. The uniformity of the displacement mechanism means that if everything is annotated with a level, then everything moves up at the same time. Previously, floating functions allowed us to essentially ignore increasing levels due to displacement, but also independently increase them via cumulativity.
Of course, we could simply define a second $\mathsf{\textcolor{#bf616a}{CPS}}$ definition with a different level, i.e. $\Pi X \mathbin{:}^{j} \star \mathpunct{.} \Pi f \mathbin{:}^{0} (\Pi a \mathbin{:}^{0} A \mathpunct{.} X) \mathpunct{.} X$ for the level $j$ that we need, but then to avoid code duplication, we’re back at needing some form of level polymorphism over $j$.
So far, I’ve treated level polymorphism as if it were something unpleasant to deal with and difficult to handle, and this is because level polymorphism is unpleasant and difficult. On the useability side, I’ve found that level polymorphism in Agda muddies the intent of the code I want to write and produces incomprehensible type errors while I’m writing it, and I hear that universe polymorphism in Coq is a similar beast. Of course, StraTT is still far away from being a useable tool, but in the absence of more complex level polymorphism, we can plan and design for more friendly elaboration-side features such as level inference.
On the technical side, it’s unclear how you might assign a type and level to something like $\forall k \mathpunct{.} \Pi x \mathbin{:}^{k} A \mathpunct{.} B$, since $x$ essentially quantifies over derivations at all levels. We would also need bounded quantification from both ends for level instantiations to be valid. For instance, $\Pi x \mathbin{:}^{k} (\Pi y \mathbin{:}^{2} A \mathpunct{.} B \; y) \mathpunct{.} C \; x$ requires that the level $k$ of the type of the domain be strictly greater than $2$, so the quantification is $\forall k > 2$; and flipping the levels, $\Pi x \mathbin{:}^{2} (\Pi y \mathbin{:}^{k} A \mathpunct{.} B \; y) \mathpunct{.} C \; x$ would require $\forall k < 2$. While assigning a level to a quantification bounded above is easy (the level of the second type above can be $3$), assigning a level to a quantification bounded below is as unclear as assigning one to an unbounded quantification.
At this point, we could either start using ordinals in general for our levels and always require upper-bounded quantification whose upper bound could be a limit ordinal, or we can restrict level quantification to prenex polymorphism at definitions, which is roughly how Coq’s universe polymorphism works, only implicitly, and to me the more reasonable option:
We believe there is limited need for […] higher-ranked universe polymorphism for a cumulative universe hierarchy.
― Favonia et al. (2023)^{9}
With the second option, we have to be careful not to repeat the same mistakes as Coq’s universe polymorphism. There, by default, every single (implicit) universe level in a definition is quantified over, and every use of a definition generates fresh level variables for each quantified level, so it’s very easy (albeit somewhat artificial) to end up with exponentially-many level variables to handle relative to the number of definitions. On the other hand, explicitly quantifying over level variables is tedious if you must instantiate them yourself, and it’s tricky to predict which ones you really do want to quantify over.
Level polymorphism is clearly more expressive, since in a definition of type $\forall i, j \mathpunct{.} \Pi x \mathbin{:}^{i} A \mathpunct{.} \Pi y \mathbin{:}^{j} B \mathpunct{.} C \; x \; y$ for instance, you can instantiate $i$ and $j$ independently, whereas displacement forces you to always displace both by the same amount. But so far, it’s unclear to me in what scenarios this feature would be absolutely necessary and not manageable with just floating functions and futzing about with the level annotations a little.
The variant of StraTT that the paper covers is the one with stratified dependent functions, nondependent floating functions, and displacement. We’ve proven type safety, mechanized a model of StraTT without floating functions (but not the interpretation from the syntax to the semantics), and implemented StraTT extended with datatypes and level/displacement inference.
The paper doesn’t yet cover any discussion of RaTT with relaxed level constraints. I’m still deliberating whether it would be worthwhile to update my mechanized model first before writing about it, just to show that it is a variant that deserves serious consideration, even if I’ll likely discard it at the end. The paper doesn’t cover any discussion on level polymorphism either, and if I don’t have any more concrete results, I probably won’t go on to include it.
It doesn’t have to be for this current paper draft, but I’d like to have a bit more on level inference in terms of proving soundness and completeness. Soundness should be evident (we toss all of the constraints into an SMT solver), but completeness would probably require some ordering on different level annotations of the same term such that well-typedness of a “smaller” annotation set implies well-typedness of a “larger” annotation set, formalizing the inference algorithm, then showing that the algorithm always produces the “smallest” annotation set.
Daniel Leivant. Stratified polymorphism. LICS 1989. doi:10.1109/LICS.1989.39157. ↩
For the semantically-inclined, it is possible to construct a model of these universes, as I’ve done in this Agda model, which also sketches out how the rest of consistency might be proven. ↩
Conor McBride. Crude but Effective Stratification. 2011. https://mazzo.li/epilogue/index.html%3Fp=857.html. ↩
This domain covariance prevents us from extending the above model with floating function types, because this behaviour semantically… well, kind of doesn’t make sense. So consistency with floating functions is an open problem. ↩
Thanks to Yiyun Liu for pointing this out about the system. ↩
The Agda model could probably be tweaked to accommodate this new system; I just haven’t done it. ↩
Or by using the implementation to type check for me, which is what I did. ↩
Favonia, Carlo Angiuli, Reed Mullanix. An Order-Theoretic Analysis of Universe Polymorphism. POPL 2023. doi:10.1145/3571250. ↩
Work | Summary |
---|---|
Nakano [0] | STLC + recursive types + guarded types |
Birkdeal, Møgelberg, Schwinghammer, Stovring [1] | dependent types + recursive types + guarded types |
Atkey and McBride [2] | STLC + recursive types + guarded types + clocks |
Birkedal and Møgelberg [3] | dependent types + guarded types |
Møgelberg [4] | dependent types + guarded types + clocks |
GDTT [6] | dependent types + guarded types + clocks + delayed substitution |
CloTT [7] | dependent types + guarded types + ticks + clocks |
GCTT [8] | cubical type theory + guarded types + delayed substitution |
TCTT [9] | cubical type theory + guarded types + ticks |
CCTT [10] | cubical type theory + guarded types + ticks + clocks |
Guarded types were first introduced by Nakano [0] in an STLC with recursive types and subtyping such that the following hold (using the modern ⊳ notation):
A ≼ ⊳A
A → B ≼ ⊳A → ⊳B ≼ ⊳(A → B)
Modern type systems present the guarded modality as an applicative functor instead (laws omitted below), together with a guarded fixpoint operator:
next : A → ⊳A
ap : ⊳(A → B) → ⊳A → ⊳B
dfix : (⊳A → A) → ⊳A
dfix f ≃ next (f (dfix f))
fix : (⊳A → A) → A
fix f ≝ f (dfix f) -- ≃ f (next (fix f))
Given some endofunctor F
on types, μ(F ∘ ⊳)
is a fixpoint of F ∘ ⊳
;
the anamorphism is defined through fix
.
We then have the tools for programming with guarded recursive types μ(F ∘ ⊳)
.
Birkdeal, Møgelberg, Schwinghammer, and Stovring [1]
present a model of guarded dependent type theory,
then providing the tools for proving with guarded recursive types.
To coprogram with coinductive types,
Atkey and McBride [2] index the modality and the above constructs by a clock κ
,
and show that νF ≝ ∀κ. μ(F ∘ ⊳ᴷ)
is a cofixpoint of F
.
Concretely, for example, μX. A × ⊳X
is the type of guarded streams of A
,
while ∀κ. μX. A × ⊳ᴷX
is the type of coinductive streams of A
.
This system additionally requires that F
commute with clock quantification
(which can be shown metatheoretically for strictly positive functors F
),
as well as a construct that permits immediately “running” a clock that was just quantified.
force : (∀κ. ⊳ᴷA) → (∀κ. A)
This system disallows instantiating a clock quantification using a clock that’s free in the type, which
disallows us from using the same clock variable twice, preventing multiple “time-streams” from becoming conflated
but Bizjak and Møgelberg [5] remove this restriction by giving a model in which two clocks can be “synchronized”.
Birkedal and Møgelberg [3]
show that in a dependently-typed system,
guarded recursive types can be defined as guarded recursive functions on types,
and Møgelberg [4]
shows that coinductive types can be too using clock quantification.
More precisely, since fix
can act on types, the cofixpoint type itself can be defined in terms of it too:
▸ᴷ : ⊳ᴷ𝒰 → 𝒰
▸ᴷ (nextᴷ A) ≃ ⊳ᴷA
νF ≝ ∀κ. fixᴷ (F ∘ ▸ᴷ)
νF ≃ F νF
These dependent type theories provide the following distributive law of modalities over dependent functions:
d : ⊳((x : A) → B) → (x : A) → ⊳B
d (nextᴷ f) ≃ nextᴷ ∘ f
This isn’t the corresponding notion of ap
for dependent functions,
but given a function f : ⊳((x : A) → B)
and an argument x : ⊳A
,
what would the type of the application of f
to x
be?
Bizjak, Grathwohl, Clouston, Møgelberg, and Birkedal [6]
resolve this issue with a notion of delayed substitution in their Guarded Dependent Type Theory (GDTT),
which waits for x
to reduce to next a
to substitute into B
.
Alternatively, and apparently more popularly,
Bahr, Grathwohl, and Møgelberg [7]
introduce a notion of ticks, which are inhabitants of clocks,
in their Clocked Type Theory (CloTT).
Notably, the ticked modality acts like quantification over ticks,
and CloTT provides reduction semantics to guarded types and guarded fixpoints.
The postulated constructs of guarded type theory can be implemented using ticks,
and there is a special ⬦
that can be applied to ticks whose clock isn’t used elsewhere in the context.
next : ∀κ. A → ⊳(α : κ). A
nextᴷ a _ ≝ a
ap : ∀κ. ⊳(α : κ).(A → B) → ⊳(α : κ).A → ⊳(α : κ).B
apᴷ f a α ≝ (f α) (a α)
dfix : ∀κ. (⊳(α : κ).A → A) → ⊳(α : κ).A
dfixᴷ f ⬦ ⇝ f (dfixᴷ f)
fix : (⊳(α : κ).A → A) → A
fixᴷ f ≝ f (dfixᴷ f)
▸ᴷ : ⊳(α : κ).𝒰 → 𝒰
▸ᴷ A ≝ ⊳(α : κ).(A α)
force : (∀κ. ⊳(α : κ).A) → (∀κ. A)
force f κ ≝ f κ ⬦
There is still one significant deficiency in the guarded type theories so far: reasoning about bisimulation using the usual identity type remains difficult. Recently, there has been a trend of augmenting guarded type theories with cubical path types (or rather, augmenting cubical type theory with guarded types): Guarded Cubical Type Theory (GCTT) [8] is a cubical variant of GDTT, Ticked Cubical Type Theory (TCTT) [9] is a cubical variant of CloTT without clock quantification, and Clocked Cubical Type Theory (CCTT) [10] is a cubical variant of CloTT with clock quantification. In all of these, the cubical path type corresponds to bisimilarity of guarded recursive types and guarded coinductive types. Alternatively, McBride [11] shows that observational equality can also implement bisimilarity.
There have been several theses and dissertations on the topic of guarded types that deserve mentioning, some of which cover some of the papers already mentioned above. The following are just the ones I know of.
Vezzosi’s licentiate thesis [12] collects together two separate works. The first is a mechanized proof of strong normalization of a simply-typed variant of Birkedal and Møgelberg’s system [3]. The second extends a clocked, guarded dependent type theory by defining a well-order on clocks (which they call Times) and additionally adds existential quantification to enable encoding inductive types. In fact, the system resembles sized types more than it does guarded types, save for the fact that inductives and coinductives are still encoded as existentially and universally clock-quantified guarded fixpoints. Because in sized types, fixpoints only reduce when applied to inductive constructor head forms, it’s unclear to me how reduction in their system behaves. Finally, just as in my own thesis, they note that to encode arbitrarily-branching inductive types, a limit operator on Time is required, which continue to believe would render the order undecidable.
Grathwohl’s PhD dissertation [13] mainly deals with GDTT and GCTT. It also includes the guarded λ-calculus from an earlier paper, which adds to a guarded STLC a second modal box operator that acts like Atkey and McBride’s clock quantification [2] but as if there is only ever one clock. The dissertation ends with an alternative to GDTT that appears to be an early variant of CloTT.
Bizjak’s PhD dissertation [14] also includes GDTT as well as the same guarded λ-calculus. It provides two different models of guarded types, one of which is the model for clock synchronization [5], and gives applications to modelling System F with recursive types and nondeterminism.
Paviotti’s doctoral thesis [15] is an application of GDTT in synthetic guarded domain theory to give denotation semantics to PCF (which includes the Y combinator) and FPC (which includes recursive types).
[0] Nakano, Hiroshi. A Modality for Recursion. (LICS 2000). ᴅᴏɪ:10.1109/lics.2000.855774.
[1] Birkedal, Lars; Møgelberg, Rasmus Ejlers; Schwinghammer, Jans; Stovring, Kristian. First Steps in Synthetic Guarded Domain Theory: .Step-Indexing in the Topos of Trees. (LICS 2011). ᴅᴏɪ:10.1109/LICS.2011.16.
[2] Atkey, Robert; McBride, Conor. Productive Coprogramming with Guarded Recursion. (ICFP 2013). ᴅᴏɪ:10.1145/2500365.2500597.
[3] Birkedal, Lars; Møgelberg, Rasmus Ejlers. Intensional Type Theory with Guarded Recursive Types qua Fixed Points on Universes. (LICS 2013). ᴅᴏɪ:10.1109/LICS.2013.27.
[4] Møgelberg, Rasmus Ejlers. A type theory for productive coprogramming via guarded recursion. (LICS 2014). ᴅᴏɪ:10.1145/2603088.2603132.
[5] Bizjak, Aleš; Møgelberg, Rasmus Ejlers. A Model of Guarded Recursion With Clock Synchronisation. (MFPS 2015). ᴅᴏɪ:10.1016/j.entcs.2015.12.007.
[6] Bizjak, Aleš; Grathwohl, Hans Bugge; Clouston, Ranald; Møgelberg, Rasmus Ejlers; Birkedal, Lars. Guarded Dependent Type Theory with Coinductive Types. (FoSSaCS 2016). ᴅᴏɪ:10.1007/978-3-662-49630-5_2.
[7] Bahr, Patrick; Grathwohl, Hans Bugge; Møgelberg, Rasmus Ejlers. The Clocks Are Ticking: No More Delays! (LICS 2017). ᴅᴏɪ:10.1109/LICS.2017.8005097.
[8] Birkedal, Lars; Bizjak, Aleš; Clouston, Ranald; Grathwohl, Hans Bugge; Spitters, Bas; Vezzosi, Andrea. Guarded Cubical Type Theory. (Journal of Automated Reasoning, 2019). ᴅᴏɪ:10.1007/s10817-018-9471-7.
[9] Møgelberg, Rasmus Ejlers; Veltri, Niccolò. Bisimulation as Path Type for Guarded Recursive Types. (POPL 2019). ᴅᴏɪ:10.1145/3290317.
[10] Kristensen, Magnus Baunsgaard; Møgelberg, Rasmus Ejlers; Vezzosi, Andrea. Greatest HITs: Higher inductive types in coinductive definitions via induction under clocks. (LICS 2022). ᴅᴏɪ:10.1145/3531130.3533359.
[11] McBride, Conor. Let’s see how things unfold: reconciling the infinite with the intensional. (CALCO 2009). ᴅᴏɪ:10.1007/978-3-642-03741-2_9.
[12] Vezzosi, Andrea. Guarded Recursive Types in Type Theory. (Licentiate thesis, 2015). https://saizan.github.io/vezzosi-lic.pdf.
[13] Grathwohl, Hans Bugge. Guarded Recursive Type Theory. (PhD dissertation, 2016). https://hansbugge.dk/pdfs/phdthesis.pdf.
[14] Bizjak, Aleš. On Semantics and Applications of Guarded Recursion. (PhD dissertation, 2016). https://abizjak.github.io/documents/thesis/semantics-applications-gr.pdf.
[15] Paviotti, Marco. Denotational semantics in Synthetic Guarded Domain Theory. (Doctoral thesis, 2016). https://mpaviotti.github.io/assets/papers/paviotti-phdthesis.pdf.
This post is a continuation of the investigation into
U+237C ⍼ RIGHT ANGLE WITH DOWNWARDS ZIGZAG ARROW.
Many thanks to Barbara Beeton, James David Mason, Anders Berglund, David Bolton, Andy Whyte,
the Rare Books staff at the Cambridge University Library, and Bob Richardson at the St Bride Library.
I’ve summarized a chronological timeline in the previous post, but here are the highlights in reverse chronological, corresponding roughly to the order in which I’ve discovered the information:
Further investigation into various glyph registries and entity tables yielded no additional information.
About a year later, I went over everything I knew again and started looking for new leads. The rest of this post collects together the live Twitter updates I had been posting during this process.
The ISO standards site tells us that TR 9573-13 was the responsibility of subcommittee 34 (SC 34) under Joint Technical Committee 1 (JTC 1) of ISO/IEC. A fortuitous search led to a historical account of JTC 1/SC34, originally compiled by James David Mason, who was vice-chairman of SC 34. Given the age of the document, I doubted the email address listed for him was up to date, but eventually I found him on Linkedin, which indicated that he’s a co-chair for the Balisage Conference. I contacted the conference chair, who put me in contact with Mason.
From the historical account and Mason himself, I’ve found that working group 1 (WG 1) of SC 34 was responsible for ISO 8879, the SGML standard, as well as TR 9573-13. The main people working on these were Charles Goldfarb, the inventor of SGML, and Anders Berglund, who was responsible for TR 9573-13. The entire SC 34 committee records are now at the Charles Babbage Institute Archives at the University of Minnesota, and consists of 9.5 cubic feet of material in 10 boxes (!). Luckily, I wouldn’t have to fly to Minneapolis to sift through all of these records, because eventually Mason managed to find me a current email address for Berglund.
Berglund tells me that the entity sets for TR 9573-13 come from three sources:
Our glyph comes from Monotype under the matrix serial number S16139.
Unfortunately (but reasonably, as all of this is from three decades ago), Berglund doesn’t have any notes on which Monotype catalogues were referenced. However, I’ve separately confirmed that the symbol is indeed from Monotype from their archives. Although the Type Archive, which held the Monotype Collection, is now shutting down, the Science Museum Group has taken photographs of the collection. There are over 5000 punches and matrices in the collection, but I was extremely lucky with my search keywords and happened upon a set of punches, Extraneous sorts (L231)…
… which contains that very sort.
The SMG holds one catalogue, the Specimen Book of ‘Monotype’ Printing Type. Its index does list L231 as an “Extraneous sorts” series, but those specimen sheets aren’t included in this book.
While looking for other catalogues that Monotype have published, I came across Alembic Press’ collection of Monotype publications. I contacted David Bolton at the press for help with the catalogues, who got back to me with a list of publications of lists of signs that do not contain S16139. Since the serial number begins with S, it should be listed as a mathematical special sign, but it was not found in any of:
Although the serial numbers in 4-Line Mathematics do go past S16139, it excludes several ranges such as S16137 – S16237 and S18325 – S18347, likely characters not involved in 4-line mathematical typesetting or were specialized commissioned characters.
Many signs were for individual customers, so might not merit being published in a list, although for example I happen to have signs S2120 to S2125, which were only for Jesus College Cambridge Boat Club as far as I know, but which do feature in the 1947 list.
Alembic Press lists, but does not possess, one final document, List of Mathematical Characters. However, it can be found in the Morison Collection at the Cambridge University Library. According to their catalogue, they have three documents under this name:
I contacted the Rare Books department, who sent me photographs of two documents with this name. The first, under Morison.MC.D25, is List of mathematical characters: ‘Monotype’ 4-line Mathematics Series 569 & L231, ‘Monophoto’ Times Mathematics Series 569B & L231B. The second, under 1972.12.177, is L231 and L231B (July 1972), a set of 21 sheets meant to be inserted at the end of the List. Since S16139 was found in the set of punches of Extraneous Sorts in series L231, I believe it may appear within these 21 sheets.
After this story received some attention from HackerNews (again), Cambridge alumnus Andy Whyte contacted me and offered to go visit and take photographs of the items in person, and determined that S16139 is indeed in 1972.12.177 on the sixth sheet. I requested a digital scan of that page, which cost 18£.
There it is, on the third-to-last row of the second column: S16139. Given its similarity to the photo that Berglund sent me, I think this is the very document from his notes.
St Bride Library is a library dedicated to the history of print, typography, and design, and they hold a number of Monotype documents. I’ve contacted them about some of these documents, and Bob Richardson got back to me with some information. Here are, I believe, the relevant ones:
As I’m likely physically visiting London next year for other purposes, I might also visit St Bride Library to take a look at these documents, especially the last one. I’m also interested in the whimsically-titled Mathematical sorts in continuous creation: verses written partly in mathematical notation by Arthur H. Phillips from 1957.
I was also informed about the state of the documents that were formerly at the Type Archive:
All of the Monotype records relating to special characters have now been removed from the Type Archive site at Stockwell. The documents were palletised for transfer to the National Collections Centre at Wroughton several months ago and are completely inaccessible. The pallets will be stored in climate controlled conditions at their new home but I cannot imagine that it would be easy to gain access since this would involve a fork-lift truck and considerable manpower.
The Type Archive did hold some paper records for the kind of character you describe, but they were of a technical nature and would not have provided much more than a reference number, a date of creation and details of the point sizes and faces in which the character was available. I cannot recall seeing any details of a specific customer for any special characters, other than in the Typographical Committee records.
It appears there’s only a single surviving volume of the Typographical Committee Minutes from 1959 to 1964, which only contains “typefaces and decorative (border) materials and things like National Emblems”. I think these committee records would’ve been my best bet at uncovering the history of the mathematical sorts — if they had still existed.
]]>Washington Square — 345 S 12th Street
One of Philly’s largest and oldest queer bookstores!
They carry both used books and a lot of new queer fiction,
as well as some secondhand clothing and other miscellany.
South Street — 704 South Street
A small volunteer-run leftist anarchist bookstore.
They have a lot of leftist materials you wouldn’t see elsewhere and a lot of zines.
Queen Village — 529 Bainbridge Street
With an unassuming storefront,
they have a HUGE two-storey warehouse of books in the back.
This is probably the largest used bookstore in Philly.
Bella Vista — 1010 S 9th Street
What can I say?
It’s a small bookstore with records (or a record store with books) and good vibes.
Logan Square — 311 N 20th Street
A used bookstore I think associated with the public libraries of Philly.
University City — 220 S 40th Street
The nearest used bookstore to campus, which is super convenient for me.
Sometimes there’s a bookstore cat hanging around!
Open surprisingly late.
University City — 3920 Spruce Street
A Victorian rowhouse turned used bookstore with teetering towers of books.
The fiction selection upstairs is very large and tends to be older and more literary.
Old City — 7 N 2nd Street
A true labyrinth of a bookstore, with a similarly large and old collection of fiction upstairs.
They also have a lovely bookstore cat!
Spruce Hill — 4530 Baltimore Avenue
Just a quaint little bookstore, what more could you ask for?
South Street – 530 S 4th Street
A very cool shop full of local art!
They’ve got zines, stickers, pins, keychains, cards, prints, clothes, and so on.
Fishtown – 2158 E Dauphin Street
Imagine goth meets vintage.
You’ll find jewellery and trinkets of that aesthetic, spiders and skulls and sigils,
and some amazing taxidermy pieces too.
South Street – 534 South Street
High quality, curated vintage clothing and other oddities.
Note that they very much emphasize not being a thrift store!
Old City – 142 North 2nd Street
Less clothing but a wider range of cute vintageware,
including toys and household goods.
Fishtown; Spruce Hill; Graduate Hospital
Similar to A Four Foot Prune,
there’s three locations of antique furniture, houseware, and trinkets.
South Street – 710 S 5th Street
Sister store to Giovanni’s Room,
with a larger selection of clothing and houseware,
and a decent amount of books as well.
Centre City – 1520 Chestnut Street
A more traditional thrift clothing store with racks and racks and racks of clothes.
Manayunk – 4165 Main Street
A combination plant store and teashop.
Yes I know I said I wouldn’t include any food places but this place is really cute and I couldn’t not.
Here I collect a list of the shops that are as good as Rain or Shine, ordered from west to east. Prices are for the smallest (non-kid’s) scoop in a cup, including 8% tax, excluding tip. This isn’t a total ranking of the ice cream shops, because I think they’re all worthwhile to visit if you want ice cream. However, I’ve put stars 🌟 next to the ones I especially like, so if you find yourself equidistant between an entry with a star and one without, do go for the one with a star instead of flipping a coin. I’ve also put pins 📍 next to the ones that are local to metro Philly, as opposed to chains that can be found in other major US cities.
This list only has shops specializing in ice cream scoops (which they call “hard” or “frozen” ice cream here, for some reason, as if anyone would want unfrozen ice cream). I will not include soft serve, rolled ice cream, shaved ice, or large ice cream chains like Ben & Jerry’s or Häagen-Dazs — if I can buy a pint at a grocery store back in Vancouver, there’s no point including it on a Philly-specific list. If you’re curious about where I might visit next, my map of ice cream shops includes both places on this list and places I’ve yet to visit. Photos can be found in the Twitter thread above, or in my Instagram guide.
265 S 44th Street (bw Spruce & Locust)
Single scoop: 4.99$
2827 W Girard Avenue
Single scoop: 4.32$
2623 W Girard Avenue
Single scoop: 4$
229 S. 20th Street (bw Walnut & Locust)
Single scoop: 6.48$
1901 Chestnut Street
“Single” scoop (two flavours): 6.75$
115 S 18th Street / 119 S 13th Street
Single scoop: 6.21$
263 S 17th Street
Single scoop: 4.90$
1716 Chestnut Street
Small gelato: 5.94$
9 E Passyunk Avenue (by Dickinson)
Single scoop: 5.94$
2 E Passyunk Avenue (by 13th & Moore)
Single scoop (w/ topping and drizzle): 5.40$
903 S 9th Street (by Christian)
Single scoope: 5$
618 S 5th Street (by South)
Single scoop: 4.50$
2311 Frankford Avenue (by Dauphin)
Single scoop: 5$
1255 E Palmer Street (on Thompson)
Single scoop: 5$
6616 Germantown Avenue
Single scoop: 5.94$
4369 Main Street
Single scoop: 5.12$
Society Hill — 410 S 2nd Street
Oat milk ice cream: 5.40$
A coffee shop that makes their own oat milk
and a very thick, pudding-like delicious oat ice cream.
Manayunk — 4165 Main Street
Rose and saffron ice cream: 4.86$
Not in the main list since it’s not an ice cream shop,
but for some reason this teahouse-turned-plantstore has one (1) single ice cream flavour
and it’s really good??
Chinatown — 202 N 9th Street (by Race)
Soft serve: 7.29$
They have a matcha and a hojicha flavour.
A little ridiculously expensive, but the flavour is excellent and very strong.
Centre City — 243 Market Street
Single scoop: 6.48$
According to their site, they use milk from their own cows, which I think is very unique and impressive.
A shame that the ice cream is incredibly melty, way too sugary, and overall not that good.
Even though I spent a good amount of time repairing the physical machine in which Thulium lived, in anticipation of moving to an entirely different country for my PhD (I wasn’t going to physically lug a desktop tower with me everywhere), I decided to move Thulium onto a VPS with Hetzner in October 2021. I was also earning an income as a Master’s student, so I felt justified in spending money monthly on what barely constitutes a hobby (I’m glad to be in a position where 7€/month is now peanuts to me). I currently have the following with Hetzner:
I may turn the storage box into an attached volume later on. It’s a little more expensive at 0.04€/GB for a total of 4€/month, but a little more flexible with the storage size, and more convenient to access since the storage box doesn’t really have SSH access.
So far, using a VPS has worked quite well with the added bonus that
Thulium doesn’t go down every time my ISP decides to change my home IP address.
There is some lag when SSHing into the server,
probably because it’s physically located in Germany,
but the internet connection is so much faster—apt update
practically finishes instantaneously
compared to when it was in a box sitting in my home.
This brings us now, though, to a Ship of Theseus question, except it’s the Server of Thulium: is it still Thulium if I’ve reinstalled the entire server on my home machine in 2020 and then moved the entire server from my home machine to a VPS, having changed out both the software and the hardware?
In October 2020, I decided to no longer renew my very first domain name, ert.space. I already had my current domain, ionathan.ch, since November 2019, and I’ve now consolidated my server’s web services and my website to both be accessed from ionathan.ch. hilb.ert.space is thus no more, although in memory of it this blog is still named ⟨ortho|normal⟩.
In the last retrospective from two (!) years ago, I listed out what I was running from my server. Now, the cohort is:
I’m no longer hosting my personal wiki, and is instead just a collection of files in a GitHub wiki. Some of these are org-mode files rather than Markdown files, because I thought I’d finally learn how to use org mode in Emacs (I thought wrong). Additionally, I’ve moved this website from GitHub Pages to GitLab Pages, since it gives me more flexibility with the Ruby plugins, which I’ve written about in a previous post.
Very recently, I’ve also set up an MTA with Postfix on my server (not through Docker) so that cron can send me emails when something goes wrong. This was spurred by the fact that I ran out of disk space for borg to create new backups, which failed silently, which resulted in a three-month gap in my backups.
It turns out it’s a lot of work to get everything running smoothly! My wiki page on MTA with Postfix has all the technical notes and annoying details. It can receive mail as well, which I didn’t originally set out to set up, but until I actually see spam incoming I’ll hold off on any more elaborate antispam measures like SpamAssassin. I’m still debating whether to also set up a MUA and some sort of mail client to interact with Postfix. Mail servers are so, so very complicated and convoluted.
Overall, I’m quite pleased with how everything is now set up. I don’t usually need to go in to maintain anything, except for updating Nextcloud when the GUI indicates there’s a new version, and now hopefully I’ll immediately be notified by cron when there is something I need to handle. Aside from considering moving the storage box to an attached volume and adding a MUA to my MTA, I don’t have any other major changes or additions planned.
]]>On the other hand, GitLab Pages is much less restrictive with what’s allowed,
because the build process is literally just a
CI script.
This bypasses the plugins allowlist, but not the configuration overrides, which are from the github-pages
Gem.
The first thing to do, then, is to forego the Gem and include only the plugins I need in Gemfile
.
On top of that, I have the Gems required to use KaTeX as kramdown’s math engine.
source "https://rubygems.org"
gem "jekyll-feed"
gem "jekyll-gist"
gem "jekyll-paginate"
gem "jekyll-remote-theme"
gem "jekyll-scholar"
gem "katex"
gem "kramdown-math-katex"
github-pages
also specifies a number of default configurations,
but most of these are either Jekyll defaults
or undesirable (namely allowlist plugins and kramdown configurations).
I also already have a number of configurations set from when I was using GH Pages.
The below are the only missing configurations that needed adding in _config.yml
to include the Jekyll plugins and to enable using KaTeX for kramdown.
I use the KaTeX option to output MathML so that I don’t need to include a stylesheet.
plugins:
- jekyll-feed
- jekyll-gist
- jekyll-paginate
- jekyll-remote-theme
- jekyll-scholar
kramdown:
math_engine: katex
math_engine_opts:
output: mathml
Finally, to use GitLab Pages, there needs to be a CI configuration script to install Node.js
(which kramdown-math-katex
needs to run KaTeX) and to build the Jekyll site.
image: ruby:latest
pages:
script:
- apt-get update && apt-get -y install nodejs
- gem install bundler
- bundle install
- bundle exec jekyll build -d public
artifacts:
paths:
- public
Now inline LaTeX works everywhere using double dollar signs:
$$\int_{\partial \Omega} \omega = \int_{\Omega} d\omega$$
yields
$\int_{\partial \Omega} \omega = \int_{\Omega} d\omega$.
There aren’t any delimiters for display-style math,
but you can always add \displaystyle
to a block of LaTeX.
For updates on the hunt for ⍼, see the new post.
Known as right angle with downwards zigzag arrow,
angle with down zig-zag arrow,
\rangledownzigzagarrow
,
and ⍼
,
no one knows what ⍼ is meant to represent or where it originated from.
Section 22.7 Technical Symbols
from the Unicode Standard on the
Miscellaneous Technical block
doesn’t say anything about it.
The original proposal that included this character is Proposal for Encoding Additional Mathematical Symbols in the BMP (N2191) from 14 March 2000, with origins from the STIX project. That project page links to a number of relevant files dating all the way back to 1997, and most importantly to the very first collation of character tables by Barbara Beeton last updated on 24 June 1997. Here we find that, in table &ac-&cirmid under codes with TR 9573 names, an entry for the character.
AFII ISO TR9573 entity ISO TR9573 description D97C ⍼ angle with down zig-zag arrow
(This table is later merged into a larger table.) A table from 18 July 1997 clarifies that these are characters from the technical report TR 9573-13. A later table from 7 Feburary 1998 with accompanying glyphs confirms that this is indeed the character we’re looking for.
Unicode Glyph SGML Name Description E248 angzarr ISOAMSA angle with down zig-zag arrow
The Unicode code point E248 is, of course, not its current actual code point. That one is located in a Private Use Area (PUA) in the Basic Multilingual Plane (BMP), and so was likely a temporary encoding within STIX before acceptance into Unicode proper. The AFII code point D97C will be explained later, as will what AFII stands for and what it is.
Related is the Mathematical Markup Language (MathML) Specification: Section 6.2.5.1 ISO Symbol Entity Sets provides the same tables as STIX, and our character can again be found in group ISO AMS-A.
The technical report, whose long name is ISO/IEC TR 9573-13:1991 Techniques for using SGML — Part 13: Public entity sets for mathematics and science, was published in July 1991; its editor was Anders Berglund. Although the ISO site claims there was never a newer version, there is a document with the same name last updated on 8 December 2003. It indeed lists U+237C in Section 7.1 isoamsa, but evidently this came after it was added to the Unicode Standard.
The actual tech report itself doesn’t provide much more information than the newer document,
shown in the capture above:
all it contains is its short name, the glyph, its old code point D97C, and a description.
(If you’re a UBC student or faculty, you can get access to the
tech report via Techstreet
or here.)
The only other reference is found in Section 6.2.5 Arrow Relations,
which gives the same entity listings as
ISOasma.ent
in the Debian package sgml-data
.
The Foreword of the tech report, though, mentions that it replaces an earlier annex.
h) Part 13 replaces ISO 8879:1986 annex D (in part)
Taking a look at ISO/IEC TR 9573:1988 Techniques for using SGML as well, which people at UBC can also access via CSA onDemand as CAN/CSA-Z243.210.1-89 (R2018), it also indicates that the symbols it uses comes from ISO 8879.
However, this Annex D of ISO 8879:1986 Standard Generalized Markup Language (SGML) (which UBC people can again access as CAN/CSA-Z243.210-89 (R2014) via CSA onDemand or here) doesn’t contain our character, neither under ISO AMS-A as expected, nor anywhere else. This does explain the category name, at least: “AMS” here stands for “Added Math Symbols”, and has nothing to do with the American Mathematical Society.
Annex D.4.4 Technical Use has a brief note on the origin of the names of the entities it does list, though.
NOTE — Visual depictions of most of the technical use entities are identified by their entity names in Association of American Publishers Electronic Manuscript Series: Markup of Mathematical Formulas, published by the Association of American Publishers, Inc., 2005 Massachusetts Avenue, N.W., Washington, DC 20036, U.S.A.
I obtained a second edition copy of Markup of Mathematical Formulas from the University of Victoria, which lists the same entities, also missing our character.
Back in TR 9573-13, Section 5 mentions that the symbols tables contain
a glyph identifier, registered in accordance with ISO/IEC 10036. The glyph identifier is shown in decimal and hexadecimal representation;
this refers to the code point D97C/55676. While ISO/IEC 10036:1993 Procedure for registration of glyph and glyph collection identifiers (accessible as CAN/CSA-ISO/IEC 10036-01 (R2014)) only describes glyph registration, the actual 10036 registry exists online. Our character can be found among code points 556xx. In 2020, the glyph table was standardized as ISO/IEC TR 10036:2020 Registered glyph identifiers, containing the same glyphs.
55676
The registry indicates that this glyph is among thousands inherited from the Association for Font Information Interchange (AFII), established in 1987 (according to this search). They had their own registry as well, cited by ISO/IEC 10036:1996, Annex D Bibliography.
Association for Font Information Interchange (AFII) International Glyph Register, Volume 1: Alphabetic Scripts and Symbols. 1993.
The AFII is long dissolved in favour of Unicode over their glyph registry; an email from Asmus Freytag, their former president, describes its history just before dissolution. Notably, it appears that anyone could register a glyph with the AFII for a fee of 5$ to 50$ (about 8.60$ to 86$, accounting for inflation).
There doesn’t seem to be a digitized version of the International Glyph Register, but thankfully, there is a copy at the Library of Congress. Additionally, Font Technology: Methods and Tools by Peter Karow (which can be found here) includes photocopies of the first 23 pages of the register in Appendix C Glyph Identifier Register. In the Introduction, Edwin Smura, the registrar, describes its contents:
The first published document of the Association for Font Information Interchange contains a glyph identifier, at least one sample glyph shape, and a description including at least a name or title for the glyph and any significant information about the meaning or intended usage.
Unfortunately, no more information is provided by the register than is already present in ISO/IEC TR 10036.
After asking on the TeX Stack Exchange about the origin and meaning of the character, Barbara Beeton herself gave a response.
I had no idea what it meant or was used for, thus assigned it a “descriptive name” when collating the symbols for the STIX project. (I still have no idea, nor can supply an example of the symbol in use.) […] it is the case that ISO 9573-13 existed long before either AFII or the STIX project were formed. […] I once asked Charles Goldfarb what the source of these entities was, but remember that he didn’t have a definitive answer.
So its appearance in AFII’s registry comes after TR 9573-13, which explains the fact that the registry was published in 1993 while the tech report in 1991, although the tech report does reference ISO/IEC 10036, whose earliest version was apparently published in 1993. It’s possible there were earlier, unpublished versions of these documents.
In a follow-up comment, Beeton says:
Although I did ask, no one was able to tell me whether there was a single source or the entities were just picked up from a number of sources.
And in reference to Half as Interesting’s video:
The information in the video is inaccurate. It fails to recognize that the inclusion of the character in the STIX collection was based on its presence in a version of ISO 9573-13 earlier than the 1991 version cited, a version which existed long before AFII was formed. So any claim that it originated with AFII is chronologically invalid.
If she was unable to find a source while actively working with AFII and STIX back in the 90s, I doubt I would be able to uncover anything new now, three decades later. Here, then, I close the book on my investigation. The meaning of ⍼ will be whatever meaning is assigned by whoever uses it next… if anyone uses it at all.
What is the glyph supposed to look like? Obviously without a definitive source, we can’t answer this question, but we can look at what various interpretations of a downwards zig-zag arrow overtop a right angle exist, starting with a vector reproduction of the glyph in the 10036 registry, originally a 96 px × 96 px GIF and supposedly copied from AFII’s registry.
The zig-zag in question has a horizontal middle bar, with the lower bar crossing the corner of the angle. Then we have the glyph from TR 9573-13, which is luckily available as a proper PDF with vector glyphs rather than a scanned document.
The zig-zag in question also contains a horizontal middle bar, but the lower bar doesn’t cross the angle corner. The arrowhead has a rather unusual shape, but is in line with the arrowheads of the other glyphs. Strangely, the vertical bar of the right angle doesn’t have a consistent width. Next is a vector reproduction of the glyph from both STIX and MathML, which were originally 32 px × 32 px GIFs.
For some reason, the middle bar of the zig-zag is now diagonal rather than horizontal. There’s also an extra pixel at the bottom bend, and the arrow now crosses the corner of the right angle. Fun fact: the horizontal bar of the right angle is a single pixel shorter than the vertical bar. Then we have the glyph as designed in the Unicode proposal document.
This one just looks like a vectorized version of the previous pixellated version. I don’t think the pixellated glyph is a downsampled version of this glyph, since the arrowhead in this glyph is larger and extends beyond the angle corner.
These last four are examples of the character in four different fonts that provide it: GNU Unifont, STIX Two, Julia Mono, and Noto Sans Math/Noto Sans Symbols. Unifont’s glyph looks like an improved version of the pixellated version: the extra pixel is gone, both bends are the same size around the vertical bar, and the arrowhead is slightly larger and more distinguishable. STIX Two’s resembles the proposal version but with a fancier arrowhead. Julia Mono’s resembles it as well, but with more acute angles and a shorter horizontal axis. (I previously said that the bottommost bar of the zig-zag wasn’t straight, and that the vertical bar of the angle had inconsistent widths; the issue has since been resolved.) Finally, Noto Sans’ glyph doesn’t even have a zig-zag; They’ve chosen to interpret the character as a wavy arrow rather than a zig-zag arrow. All of these have an arrow crossing the right angle corner, which seems to be a distinguishing characteristic of the character.
⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌
The first two and last two are part of Palmer notation for denoting human teeth by their position. What are the rest for?
The Unicode Standard, Section 22.7 Technical Symbols, doesn’t have much specific to say about the matter.
Dental Symbols. The set of symbols from U+23BE to U+23CC form a set of symbols from JIS X 0213 for use in dental notation.
Looking at the Miscellaneous Technical code chart, their names describe the appearance of the symbols but not so much their function.
23C0 ⏀ DENTISTRY SYMBOL LIGHT VERTICAL WITH CIRCLE
23C1 ⏁ DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH CIRCLE
23C2 ⏂ DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH CIRCLE
23C3 ⏃ DENTISTRY SYMBOL LIGHT VERTICAL WITH TRIANGLE
23C4 ⏄ DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH TRIANGLE
23C5 ⏅ DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH TRIANGLE
23C6 ⏆ DENTISTRY SYMBOL LIGHT VERTICAL AND WAVE
23C7 ⏇ DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL WITH WAVE
23C8 ⏈ DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL WITH WAVE
23C9 ⏉ DENTISTRY SYMBOL LIGHT DOWN AND HORIZONTAL
23CA ⏊ DENTISTRY SYMBOL LIGHT UP AND HORIZONTAL
The JIS X 0213 is yet another character set encoding standard and doesn’t explain much either. Any technical documents would probably be in Japanese, which I can’t read. Luckily, the Wikipedia page on Miscellaneous Technical keeps a pretty extensive historical record of how its characters were introduced into Unicode. According to the table, the dentistry symbols were introduced in version 3.2 originating from a proposal in 1999. Here’s where we’ll start our hunt for their meaning.
The dentistry symbols along with some circled digits were introduced in Addition of medical symbols and enclosed numbers on 13 September 1999. The document doesn’t actually explain what the symbols mean.
The Unicode Technical Committee has the same questions I do. The meeting minutes for a committee meeting over 26–29 October 1999 note:
Twenty Seven Dentist Characters
Consensus 81-C5: The circled characters will considered as part of a general mechanism as documented in 81-C4. Respond to the Shibano-san on the remaining proposed dentist symbols that we need evidence of usage. The UTC is not accepting any of the dentist symbols at this time. [L2/99-238]
Action Item 81-33 for Lisa Moore: Inform Shibano-san that we are not accepting any of the dentist symbol characters, and provide our feedback.
And so in a proposal comment on 23 November 1999, Lisa Moore, chair of the Committee, writes:
6) Twenty Seven Dentist Characters. The UTC will consider the ten double circled numbers as part of the general mechanism to be defined in the future. See 4) above. The remaining seventeen dentist symbols were not accepted due to insufficient evidence of usage. Please provide documents with examples of usage, and explain if any of these characters are combining, or if any extend across other symbols to delineate quadrants of the jaw.
The 17 symbols refer to the 15 dentistry symbols along with U+29FA ⧺ double plus and U+29FB ⧻ triple plus, which are later encoded in Miscellaneous Mathematical Symbols-B. In a proposal revision on 31 January 2000, Professor Kohji Shibano, chairman of the JCS committee, writes:
(c) Evidence of usage
You requested to submit evidence of usage for some characters. We are now preparing the requested document for the following characters with some explanation in English. This document will be sent to you as soon as possible.
Finally, in Rationale for non-Kanji characters proposed by JCS committee on 15 March 2000, the dentistry symbols are… “explained”.
(2) Dentist’s symbols
These symbols are used in dentistry when drawing XXX together with some BOX DRAWING characters. The proposal includes two types of characters; those used in single-line drawing and those in triple-line drawing.
It makes sense that the lines are meant to be used with box-drawing characters in dental notation to illustrate the teeth. But what do the circle, the triangle, and the tilde mean? The Wikipedia article on dental notation doesn’t seem to use these symbols (and nor does the corresponding German article, which is much more comprehensive).
The committees, on the other hand, seem satisfied with this explanation. The meeting minutes for an ISO/IEC subcommittee meeting over 21–24 March 2000 note a comment (Section 8.20, page 44) by Dr. Ken Whistler of Sybase, Inc.:
ii) The Dental symbols are sufficiently explained - these are box-drawing characters overlaid in specific manner.
The meeting minutes for a Unicode Technical Committee meeting on 28 April 2000 read:
[83-M3] Motion: Accept the twenty five characters documented in the report on the Beijing meeting, sections E 1, 2, 3, 5, 6, 7, 8, 9 [L2/00-108]:
⚬ Double plus sign and triple plus sign
⚬ 15 dentist symbols
So there aren’t any more explanations we can expect to see from these documents. Subsequent meeting minutes only discuss technical details. From 19–22 September 2000 (Section 7.21, page 40):
Action Items: Messrs. Michael Everson and Takayuki Sato - will provide better glyphs for the DENTIST Symbols (from document N2093). The amendment text is to be prepared by the editor.
From 9 March 2001 (Irish comments, page 5):
Table 63 (67) - Row 23: Miscellaneous Technical
The Japanese remarked in Athens that the glyphs for the dentistry symbols 23C0-23CC should fill a notional square. We have provided the editor with corrected glyphs.
And from 2–5 April 2001, more remarks on the shape of the characters (Section 7.1, page 21), and the proposed character names are changed from DENTIST to DENTISTRY (Section 7.1, page 22):
Comment 14: Shapes of JIS X0213 characters – Accepted.
Japan will supply suitable fonts. Kana has to cover all existing Kana characters also. As to the Dentist symbols, glyphs do not seem to look square. We need to know why current glyphs are not acceptable. A single font is needed for a range.
SE6: […] Rename DENTIST to DENTISTRY symbols … should it be DENTAL? Accept DENTISTRY.
On 27 March 2002, Unicode 3.2 is released. The current blurb on dentistry symbols is added as part of the Unicode Standard Annex #28; the new code points are highlighted in this code chart.
I don’t have any dentistry friends, but surely someone knows, so I asked around. I asked the Medical Sciences Stack Exchange, I asked on the Unicode mailing list, and I even threw the question out there on Twitter. In the end, though, someone I knew found it with a method so simple it never occurred to me.
And indeed, an explanation can be found right there in Dental Computing and Applications: Advanced Techniques for Clinical Dentistry by Andriani Daskalaki, published in 2009. In fact, a simple “dentistry unicode” search on Google Books takes me right there.
Of course, this isn’t the origin of the symbols, but it does explain what they mean. According to Chapter XVII on Unicode Characters for Human Dentition^{†}, the notation comes from Japan’s dental insurance claim system.
Although these signs are not specific to dentistry, we assigned a specific meaning to these modified numerical symbols in accordance with the dental insurance claim system in Japan.
…
Figure 4 shows nine characters in three groups denoting artificial teeth, supernumerary teeth and an abbreviation for a group of teeth respectively. A triangle with a bar indicates an artificial spacer, especially on a denture, a circle with a bar indicates a supernumerary tooth, and a tilde with a bar indicates an abbreviation for a group of teeth.
So that’s the end of the mystery. I should go update all the places I’ve asked with this discovery now.
Since the original publication of this post I’ve received some replies to my question posted to the Unicode mailing list, in particular Ryusei Yamaguchi’s response. Some of the sources cited agree with the above: 歯式の記載について on dental notation lists using ⏈ for describing a span of upper teeth and ⏇ for describing a span of lower teeth.
⏈: この記号の両端の歯の間に存在する全ての歯（上顎用で正中を越える）
[DeepL translation: “All teeth present between the teeth on either side of this sign (for maxillary and beyond the median)”]
⏇: この記号の両端の歯の間に存在する全ての歯（下顎用で正中を越える）
[DeepL translation: “All teeth present between the teeth on either side of this sign (for mandible and beyond the midline)”]
On the other hand, 電子レセプトの作成手引き on filing electronic dental insurance claims uses △ to indicate 「部近心隙」 (page 25), which seems to mean a diastema, or a tooth gap, so that ⏅⏃⏄ are used to indicate a gap between the front teeth, rather than artificial teeth as previously suggested. This usage is further backed up by the 歯式メーカー app, which describes using △ to indicate a gap.
△で隙を入力します。選択した歯の近心の隙を入力します。
[Google Translate: “Enter the gap with △. Enter the mesial gap of the selected tooth.”]
Finally, there doesn’t seem to be any other explanation of the circle and the usage of ⏂⏀⏁. Circled numbers indicate dental implants at the given tooth position, so it doesn’t make sense in that case for the circle to go in between teeth. It’s likely, then, that they are indeed for indicating supernumary teeth, and in particular the mesiodentes that occur between the two front teeth.
^{†}This does require access to IGI Global’s resources, but you can just find the entire book here.
]]>