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 scoop in a cup, including 8% tax.
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.

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$

1901 Chestnut Street

“Single” scoop (two flavours): 6.75$

115 S 18th Street (by Chestnut)

Single scoop: 6.21$

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$

Fishtown — 1255 E Palmer Street (on Thompson)

Ice cream sandwich: 5$

*They only do pints and ice cream sandwiches, not scoops.
But the sandwich is very good.*

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:

- A CPX11 cloud server in Nuremberg, Germany with 2 vCPUs, 2 GB RAM, 40 GB disk space, and an IPv4 address for 3.99€/month;
- An additional attached 15 GB volume at 0.60€/month for my borg backups; and
- A BX10 storage box in Berlin, Germany with 100 GB storage at 2.90€/month for my photos backup.

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:

- The same Nextcloud instance, although with a few update mishaps along the way;
- Gitbert on Gitea as always; and
- A FreshRSS instance in place of TTRSS after the latter kept breaking across updates;

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 "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.

```
plugins:
- jekyll-feed
- jekyll-gist
- jekyll-paginate
- jekyll-remote-theme
kramdown:
math_engine: katex
```

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:
`$$\displaystyle \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.

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.

AFIIISO TR9573 entityISO TR9573 descriptionD97C ⍼ 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.

UnicodeGlyphSGMLNameDescriptionE248 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.
Although their 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). ISO/IEC 10036 provides the registration process along with contact information for the AFII in Annex A Registration Authority.

Association for Font Information Interchange

Post Office Box 33683

Northglenn, Colorado 80233-0683

Telephone: +1 303 924-7670

Facismile: +1 303 451-5475

Email: afii@ix.netcom.com

Even if the *International Glyph Register* can be found,
it likely merely contains another table with the glyph, the indentifier, and the short description.
To know its origins would require the original registration request that added the character,
but it’s unlikely that such old documents from a now-defunct non-profit organization in the 90s
would have been kept or digitized.

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.

- 1983: AAP Electronic Manuscript Project
begins, publishing a series that includes
*Markup of Mathematical Formulas* - 1986-10-15: ISO 8879 (SGML) is published,
containing ISO AMS-A without ⍼, referencing
*Markup of Mathematical Formulas* - 1987-09-15: AFII is established
- 1987-11:
*Markup of Mathematical Formulas*, 2 ed. is published without ⍼, referencing ISO 8879 - 1988-12: ISO/IEC TR 9573 (Techniques for using SGML) is published, referring to ISO 8879
- 1988: ISO 8879 is revised with a new Annex J
- 1988: ANSI/NISO Z39.59 (AAP Electronic Manuscript Standard) is published, based on part of the published series, likely with no entity tables
- 1991-07-01: ISO/IEC TR 9573-13 (Public entity sets for mathematics and science) is published, containing ISO AMS-A with the first verifiable occurrence of ⍼, referencing ISO/IEC 10036
- 1993-06: ISO/IEC 10036 (Glyph registration) is published
- 1993:
*International Glyph Register*by the AFII is published, possibly containing ⍼ - 1994-09: ISO 12083 (Electronic manuscript preparation and markup) is published, based on ANSI/NISO Z39.59, with no entity tables
- 1996-07: ISO/IEC 10036 is revised,
referencing the
*International Glyph Register* - 1997-06: The STIX project begins, taking character tables from ISO/IEC TR 9573-13
- 2000-03-14: ISO/IEC JTC1/SC2/WG2 N2191 (Proposal for Encoding Additional Mathematical Symbols) is proposed, adding ⍼ to the Unicode Standard

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 is a little weird: the bottommost bar of the zig-zag isn’t straight, and the vertical bar of the angle again has inconsistent widths (although an issue’s been opened for that). 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.

*What is ⍼ used for?*on TeX SE and Math SE- Twitter thread about this post
- Ellis (), a chaos magic sigil that looks suspiciously similar to ⍼
*The Assault on Reality*, the origin of Ellis (referred to as the Linking Sigil)- Hacker News, Lobste.rs, and Hackaday discussions
- XKCD #2606 mentions ⍼ and its Explain XKCD entry cites this post
- Half as Interesting did a video on this post (don’t bother, it’s less than half as interesting)

```
⎾⎿⏀⏁⏂⏃⏄⏅⏆⏇⏈⏉⏊⏋⏌
```

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.

- Well-Founded Trees
- Indexed Well-Founded Trees
- Indexed Inductives and Fording
- Nested Inductives
- Inductive–Inductives
- Inductive–Recursives
- Indexed Well-Founded Trees as Canonized Well-Founded Trees

```
data W (A : 𝒰) (B : A → 𝒰) : 𝒰 where
sup : ∀ a → (B a → W A B) → W A B
```

`A`

selects the constructor as well as providing the constructor’s nonrecursive arguments.
`B`

then selects the recursive element as well as providing the recursive element’s arguments.

```
data Ord (A : 𝒰) : 𝒰 where
Z : A → Ord A
S : Ord A → Ord A
L : (ℕ → Ord A) → Ord A
Ord A = W (A + 𝟙 + 𝟙) B
where
B (in1 a) = 𝟘
B (in2 ∗) = 𝟙
B (in3 ∗) = ℕ
Z a = sup (in1 a) absurd
S o = sup (in2 ∗) (λ _ → o)
L f = sup (in3 ∗) f
```

```
data IW (I : 𝒰)
(A : I → 𝒰)
(B : ∀ i → A i → 𝒰)
(d : ∀ i → (a : A i) → B i a → I) :
I → 𝒰 where
isup : ∀ i → (a : A i) →
((b : B i a) → IW I A B d (d i a b)) →
IW I A B d i
```

The indexed W type can be seen as either encoding an inductive type with nonuniform parameters
or as encoding mutual inductive types, which are indexed inductive types anyway.
`I`

selects the nonuniform parameters, which I’ll call the index for now
`A`

selects the constructor, `B`

selects the recursive element,
and `d`

returns the index of that recursive element.

```
data Even : 𝒰 where
Z : Even
Sₑ : Odd → Even
data Odd : 𝒰 where
Sₒ : Even → Odd
EvenOdd = IW 𝟚 A B d
where
Even = in1 ∗
Odd = in2 ∗
A Even = 𝟚 -- Even has two constructors
A Odd = 𝟙 -- Odd has one constructor
B Even (in1 ∗) = 𝟘 -- Z has no recursive elements
B Even (in2 ∗) = 𝟙 -- Sₑ has one recursive element
B Odd ∗ = 𝟙 -- Sₒ has one recursive element
d Even (in1 ∗) = absurd
d Even (in2 ∗) ∗ = Odd
d Odd ∗ ∗ = Even
Z = isup Even (in1 ∗) absurd
Sₑ o = isup Even (in2 ∗) (λ _ → o)
Sₒ e = isup Odd ∗ (λ _ → e)
```

```
variable
T : 𝒰
_<_ : T → T → 𝒰
data Acc (t : T) : 𝒰 where
acc : (∀ s → s < t → Acc s) → Acc t
Acc t = IW T (λ _ → 𝟙) (λ t ∗ → ∃[ s ] s < t) (λ t ∗ (s , _) → s) t
```

```
data PTree (A : 𝒰) : 𝒰 where
leaf : A → PTree A
node : PTree (A × A) → PTree A
PTree = IW 𝒰 (λ A → A + 𝟙) B d
where
B A (in1 a) = 𝟘
B A (in2 ∗) = 𝟙
d A (in1 a) = absurd
d A (in2 ∗) ∗ = A × A
leaf A a = isup A (in1 a) absurd
node A t = isup A (in2 ∗) (λ _ → t)
```

So far, (nonuniformly) parametrized inductives and mutual inductives can be encoded. Indexed inductives can be encoded as well by first going through a round of fording to turn them into nonuniformly parametrized inductives. Meanwhile, mutual inductives can also be represented as nonuniform parametrized inductives by first turning them into indexed inductives.

```
variable
A B : 𝒰
data Image (f : A → B) : B → 𝒰 where
image : ∀ x → Image f (f x)
-- Forded image type
data Image' (f : A → B) (b : B) : 𝒰 where
image' : ∀ x → b ≡ f x → Image f b
Image' f b = W (∃[ x ] b ≡ f x) 𝟘
image' x p = sup (x , p) absurd
```

```
data Fin : ℕ → 𝒰 where
FZ : ∀ n → Fin (S n)
FS : ∀ n → Fin n → Fin (S n)
-- Forded finite sets type
data Fin' (m : ℕ) : 𝒰 where
FZ' : ∀ n → m ≡ S n → Fin m
FS' : ∀ n → m ≡ S n → Fin n → Fin m
Fin' = IW ℕ (λ m → 𝟚 × ∃[ n ] m ≡ S n) B d
where
B m (in1 ∗ , n , p) = 𝟘
B m (in2 ∗ , n , p) = 𝟙
d m (in1 ∗ , n , p) = absurd
d m (in2 ∗ , n , p) ∗ = n
FZ' m n p = isup m (in1 ∗ , n , p) absurd
FS' m n p fin = isup m (in2 ∗ , n , p) (λ _ → fin)
```

Nested inductive types, when represented as recursive μ types, have nested type binders. Nonindexed inductive types potentially with nonuniform parameters, on the other hand, are single μ types.

```
Ord A = μX: 𝒰. A + X + (ℕ → X)
EvenOdd = μX: 𝟚 → 𝒰. λ { in1 ∗ → 𝟙 + X (in2 ∗) ; in2 ∗ → X (in1 ∗) }
Acc = μX: T → 𝒰. λ t → ∀ s → s < t → X s
PTree = μX: 𝒰 → 𝒰. λ A → A + X (A × A)
Fin' m = μX: ℕ → 𝒰. (∃[ n ] m ≡ S n) + (∃[ n ] (m ≡ S n) × X n)
```

Nested inductives, when not nested within themselves, can be defunctionalized into indexed inductives, which can then be forded into nonuniformly parametrized inductives, which can finally be encoded as indexed W types.

```
data FTree : 𝒰 where
ftree : List FTree → FTree
FTree = μX: 𝒰. List X = μX: 𝒰. μY: 𝒰. 𝟙 + X × Y
data I : 𝒰 where
Tree : I
List : I → I
data Eval : I → 𝒰 where
nil : Eval (List Tree)
cons : Eval Tree → Eval (List Tree) → Eval (List Tree)
ftree : Eval (List Tree) → Eval Tree
data Eval' (i : I) : 𝒰 where
nil' : i ≡ List Tree → Eval' i
cons' : i ≡ List Tree → Eval' Tree → Eval' (List Tree) → Eval' i
ftree : i ≡ Tree → Eval' (List Tree) → Eval' i
Eval' = IW I A B d
where
A i = i ≡ List Tree + i ≡ List Tree + i ≡ Tree
B _ (in1 _) = 𝟘
B _ (in2 _) = 𝟚
B _ (in3 _) = 𝟙
d _ (in1 _) = absurd
d _ (in2 _) (in1 ∗) = Tree
d _ (in2 _) (in2 ∗) = List Tree
d _ (in3 _) ∗ = List Tree
```

It’s unclear how this might be encoded either as indexed inductives or as an indexed W type.

```
data Bush (A : 𝒰) : 𝒰 where
bnil : Bush A
bcons : A → Bush (Bush A) → Bush A
Bush = μX: 𝒰 → 𝒰. λ A → 𝟙 + A × X (X A)
```

While mutual inductives allow the types of constructors of multiple inductives to refer to one another, inductive–inductives further allow one inductive to be a parameter or index of another.

```
data A : 𝒰 where
…
data B : A → 𝒰 where
…
```

That is, the entries of a context must be well formed under the correct context, while the context under which types are well formed must themselves be well formed.

```
data Ctxt : 𝒰 where
· : Ctxt
_∷_ : ∀ Γ → Type Γ → Ctxt
data Type : Ctxt → 𝒰 where
U : ∀ Γ → Type Γ
Var : ∀ Γ → Type (Γ ∷ U Γ)
Pi : ∀ Γ → (A : Type Γ) → Type (Γ ∷ A) → Type Γ
```

To encode this inductive–inductive type, it’s split into two mutual inductives:
an “erased” one with the type interdependency removed (i.e. `Type'`

does not have a `Ctxt'`

parameter),
and one describing the relationship between the two.

```
data Ctxt' : 𝒰 where
· : Ctxt'
_∷_ : Ctxt → Type → Ctxt
data Type' : 𝒰 where
U : Ctxt' → Type'
Var : Ctxt' → Type'
Pi : Ctxt' → Type' → Type' → Type'
data Ctxt-wf : Ctxt' → 𝒰 where
·-wf : Ctxt-wf ·
∷-wf : ∀ {Γ} {A} → Ctxt-wf Γ → Type-wf Γ A → Ctxt-wf (Γ ∷ A)
data Type-wf : Ctxt' → Type' → 𝒰 where
U-wf : ∀ {Γ} → Ctxt-wf Γ → Type-wf Γ (U Γ)
Var-wf : ∀ {Γ} → Ctxt-wf Γ → Type-wf (Γ ∷ U Γ) (Var Γ)
Pi-wf : ∀ {Γ} {A B} → Ctxt-wf Γ → Type-wf Γ A →
Type-wf (Γ ∷ A) B → Type-wf Γ (Pi Γ A B)
```

In other words, `Ctxt'`

and `Type'`

describe the syntax,
while `Ctxt-wf`

and `Type-wf`

describe the well-formedness rules.

```
Γ ⩴ · | Γ ∷ A (Ctxt')
A, B ⩴ U | Var | Π A B (Type' with Ctxt' argument omitted)
─── ·-wf
⊢ ·
⊢ Γ Γ ⊢ A
────────── ∷-wf
⊢ Γ ∷ A
⊢ Γ
────────── U-wf
Γ ⊢ U type
⊢ Γ
──────────────── Var-wf
Γ ∷ U ⊢ Var type
⊢ Γ Γ ⊢ A Γ ∷ A ⊢ B
───────────────────── Pi-wf
Γ ⊢ Π A B type
```

The final encoding of a context or a type is then the erased type paired with its well-formedness.

```
Ctxt = Σ[ Γ ∈ Ctxt' ] Ctxt-wf Γ
Type (Γ , Γ-wf) = Σ[ A ∈ Type' ] Type-wf Γ A
· = · , ·-wf
(Γ , Γ-wf) ∷ (A , A-wf) = Γ ∷ A , ∷-wf Γ-wf A-wf
U (Γ , Γ-wf) = U Γ , U-wf Γ-wf
Var (Γ , Γ-wf) = Var Γ , Var-wf Γ-wf
Pi (Γ , Γ-wf) (A , A-wf) (B , B-wf) = Pi Γ A B , Pi-wf Γ-wf A-wf B-wf
```

These indexed mutual inductives can then be transformed into a single indexed inductive with an additional index,
then into a nonuniformly parametrized inductive, and finally into an indexed W type.
The same technique can be applied to generalized inductive–inductives, e.g. “infinitary” `Pi`

.

```
data Type' : 𝒰 where
…
Pi∞ : Ctxt' → (ℕ → Type') → Type'
data Type-wf : Ctxt' → Type' → 𝒰 where
…
Pi∞-wf : ∀ {Γ} {T : ℕ → Type'} → Ctxt-wf Γ →
(∀ n → Type-wf Γ (T n)) → Type-wf Γ (Pi∞ Γ T)
Pi∞ (Γ , Γ-wf) TT-wf = Pi∞ Γ (fst ∘ TT-wf) , Pi∞-wf Γ-wf (snd ∘ TT-wf)
```

You can’t encode these as W types apparently.

*This section is lifted from Dan Doel’s encoding
of indexed W types as W types following the canonical construction from
Why Not W? by Jasper Hugunin.*

An indexed W type can be encoded as an unindexed one by first storing the index
together with the `A`

type as in `IW'`

below.
Then, define the `canonical`

predicate to assert that, given some index selector `d`

as would be found in an indexed well-founded tree,
not only is the current index the one we expect,
but the index of all recursive elements are the ones dictated by `d`

.
That is, `f b`

gives the actual recursive element from which we can extract the index,
while `d i a b`

gives the expected index, and we again assert their equality.
Finally, an encoded indexed W type `EIW`

is a `IW'`

type such that the index is canonical.

```
variable
I : 𝒰
A : I → 𝒰
B : ∀ i → A i → 𝒰
d : ∀ i → (a : A i) → B i a → I
IW' (I : 𝒰) →
(A : I → 𝒰) →
(B : ∀ i → A i → 𝒰) → 𝒰
IW' I A B = W (∃[ i ] A i) (λ (i , a) → B i a)
canonical : (∀ i → (a : A i) → B i a → I) → IW' I A B → I → 𝒰
canonical d (sup (i , a) f) i' = (i ≡ i') × (∀ b → canonical d (f b) (d i a b))
EIW : (I : 𝒰) →
(A : I → 𝒰) →
(B : ∀ i → A i → 𝒰) →
(d : ∀ i → (a : A i) → B i a → I) → I → 𝒰
EIW I A B d i = Σ[ w ∈ IW' I A B ] (canonical d w i)
isup : (i : I) → (a : A i) → ((b : B i a) → EIW I A B d (d i a b)) → EIW I A B d i
isup i a f = sup (i , a) (fst ∘ f) , refl i , (snd ∘ f)
```

Untyped conversion (and therefore reduction), I think, is meant to model the implementation of a conversion checker. (I’m really not the best person to ask.) Ideally, you’d want it to be entirely decoupled from the type checker, which is a very Software Engineering 110 reasonable thing to expect. An implementation outline might look like this:

- Reduce both terms sufficiently.
- If they look different, give up.
- Recur on subterms.

“Sufficiently” might mean normal form or weak head normal form or whatever reasonable form you like. So we might formalize that as follows:

```
───────────────────────── β
(λx: τ. e) e' ⊳ e[x ↦ e']
────── ⊳*-refl
e ⊳* e
e₁ ⊳ e₂
e₂ ⊳* e₃
──────── ⊳*-trans
e₁ ⊳* e₃
eᵢ ⊳* eᵢ'
─────────────────────── ⊳*-cong
e[x ↦ eᵢ] ⊳* e[x ↦ eᵢ']
e₁ ⊳* e
e₂ ⊳* e
─────── ≈-red
e₁ ≈ e₂
```

The “sufficiently” part comes from `⊳*-trans`

, where you take as many steps as you need.
The congruence rules are the most tedious, since you need one for every syntactic form,
so I’ve instead lazily written them as a single substitution.
Conversion is an equivalence relation, as you’d expect:
it’s reflexive (by `⊳*-refl`

), it’s symmetric (by swapping premises in `≈-red`

),
it’s substitutive (by `⊳*-cong`

), and it’s transitive *if* reduction is confluent,
because then you can construct the conversion by where the pairs meet.
(Confluence left as an exercise for the reader.)

```
e₁ ≈ e₂ ≈ e₃
\ / \ /
e₁₂ e₂₃ ← confluence gives this diamond
\ /
e*
e₁ ⊳* e*
e₃ ⊳* e*
────────
e₁ ≈ e₃
```

Dually to β, let’s now add η-contraction, but suppose we had cumulativity
(or more generally, *any* subtyping relation).
Then η-contraction is no good, since it breaks confluence.
Supposing we had types `σ ≤ τ`

, `λx: σ. (λy: τ. f y) x`

could either β-reduce to `λx: σ. f x`

,
or η-contract with congruence to `λy: τ. f y`

, but these are no longer α-equivalent due to the type annotation.
Breaking confluence then means breaking transitivity of conversion as well.
η-contraction then isn’t an option with Church-style type-annotated intrinsically-typed terms.

What about η-expansion? If you had a neutral term typed as a function, you may expand it once. But with untyped conversion, there’s no way to tell whether the term is indeed typed as a function, and you can’t go around η-expanding any old neutral term willy-nilly.

The remaining solution is then to add η-equivalence as part of conversion. There are two ways to do this; the first is the obvious way.

```
────────────── ≈-ηₗ (+ ≈-ηᵣ symmetrically)
λx: τ. f x ≈ f
```

This immediately requires explicit transitivity and congruence rules,
since `λx: τ. λy: σ. f x y ≈ f`

wouldn’t hold otherwise.
The other way is to check that one side is a function,
then apply the other side.

```
e₁ ⊳* λx: τ. e₁'
e₂ ⊳* e₂'
x ∉ FV(e₂')
e₁' ≈ e₂' x
──────────────── ≈-ηₗ (+ ≈-ηᵣ symmetrically)
e₁ ≈ e₂
```

This looks more ideal since it seems like it easily extends the implementation outline:

- Reduce both terms sufficiently.
- If one of them looks like a function, recur according to
`≈-η`

. - If they look different, give up.
- Recur on subterms.

You then still need congruence rules for step 4;
otherwise `F G ≈ F (λX: 𝒰. G X)`

would not hold given some `F: (𝒰 → 𝒰) → 𝒰`

and `G: 𝒰 → 𝒰`

.
It seems like transitivity *might* hold without explicitly adding it as a rule,
again by confluence, but this time requiring induction on derivation heights rather than structural induction,
and first showing that the derivation of any symmetric conversion has the same height.

Suppose we were in a setting with multiple syntactic functions, for instance the Calculus of Constructions or System F, where abstraction by and application of a type differs from ordinary term abstractions and applications.

```
Γ, x: σ ⊢ e: τ Γ, α: ⋆ ⊢ e : τ
─────────────────────── ─────────────────
Γ ⊢ λx: σ. e : Πx: σ. τ Γ ⊢ Λα. e : ∀α. τ
Γ ⊢ e : Πx: σ. τ Γ ⊢ e : ∀α. τ
Γ ⊢ e' : σ Γ ⊢ σ : ⋆
──────────────────── ────────────────────
Γ ⊢ e e' : τ[x ↦ e'] Γ ⊢ e [σ] : τ[α ↦ σ]
(λx: τ. e) e' ⊳ e[x ↦ e'] (Λα. e) [σ] ⊳ e[α ↦ σ]
```

If both of these functions had η-conversion rules, transitivity wouldn’t hold,
especially for open terms.
Specifically, the conversions `λx: τ. f x ≈ f`

and `f ≈ Λα. f [α]`

are both derivable
(despite being ill-typed when considered simultaneously, since conversion is untyped),
but `λx: τ. f x ≈ Λα. f [α]`

is impossible to derive.

In Oury’s Extensional Calculus of Constructions [2],
equality reflection is added to untyped conversion
(`≡`

denoting the equality *type*).

```
Γ ⊢ p: x ≡ y
──────────── ≈-reflect
Γ ⊢ x ≈ y
```

There’s a clash between the fact that ill-typed terms can still be convertible,
and that equality reflection only makes sense when everything is well-typed.
In particular, you cannot simultaneously have congruence and transitivity of conversion,
since it allows you to derive an inconsistency.
Concretely, using an ill-typed proof of `⊤ ≡ ⊥`

(where `⊤`

is trivially inhabited by `∗`

and `⊥`

is uninhabited),
you can convert from `⊤`

to `⊥`

.

```
· ⊢ ⊤ ≈ (λp: ⊤ ≡ ⊥. ⊤) refl (by β-reduction)
≈ (λp: ⊤ ≡ ⊥. ⊥) refl (by ≈-cong and ≈-reflect on (p: ⊤ ≡ ⊥) ⊢ p: ⊤ ≡ ⊥)
≈ ⊥ (by β-reduction)
```

Note the ill-typedness of the application:
`refl`

is clearly not a proof of `⊤ ≡ ⊥`

.
Evidently this leads to a contradiction,
since you could then convert the type of `∗`

from `⊤`

to `⊥`

.

Coq’s conversion algorithm can be found in its kernel,
which is actually one giant algorithm parametrized over whether it should be checking convertibility or cumulativity.
The below is my attempt at writing it down as rules (ignoring cases related to (co)inductives),
with MetaCoq’s conversion in pCuIC as guidance.
`[ʀ]`

represents the relation over which they are parametrized,
which can be either `[≈]`

or `[≤]`

.

```
i = j
────────── ≈-𝒰
𝒰ᵢ [≈] 𝒰ⱼ
i ≤ j
────────── ≤-𝒰
𝒰ᵢ [≤] 𝒰ⱼ
τ₁ [≈] τ₂
σ₁ [ʀ] σ₂
───────────────────────── ʀ-Π
Πx: τ₁. σ₁ [ʀ] Πx: τ₂. σ₂
t₁ [ʀ] t₂
e₁ [≈] e₂
─────────────── ʀ-app
t₁ e₁ [ʀ] t₂ e₂
τ₁ [≈] τ₂
e₁ [ʀ] e₂
───────────────────────── ʀ-λ
λx: τ₁. e₁ [ʀ] λx: τ₂. e₂
τ₁ [≈] τ₂
t₁ [≈] t₂
e₁ [ʀ] e₂
───────────────────────────────────────────── ʀ-let
let x: τ₁ ≔ t₁ in e₁ [ʀ] let x: τ₂ ≔ t₂ in e₂
e₁ [ʀ] e₂
─────────────────────── (catch-all for remaining syntactic constructs)
t[x ↦ e₁] [ʀ] t[x ↦ e₂]
e₂ x ⊳* e₂'
e₁ [≈] e₂'
──────────────── ʀ-ηₗ
λx: τ. e₁ [ʀ] e₂
e₁ x ⊳* e₁'
e₁' [≈] e₂
──────────────── ʀ-ηᵣ
e₁ [ʀ] λx: τ. e₂
```

The “real” conversion and subtyping rules are then the confluent closure of the above. The actual implementation performs more reduction as needed; I think this is just for performance reasons, and because there’s no way to forsee how many steps you’ll end up having to take during initial reduction.

```
e₁ ⊳* e₁'
e₂ ⊳* e₂'
e₁' [≈] e₂'
───────────
e₁ ≈ e₂
e₁ ⊳* e₁'
e₂ ⊳* e₂'
e₁' [≤] e₂'
───────────
e₁ ≤ e₂
```

Reflexivity and symmetry of conversion and reflexivity of subtyping are easy to see. Congruence is built into the rules (shown with the same substitution notation as before). Evidently conversion implies subtyping, but this time indirectly.

[1] McBride, Conor. (9 January 2015). *universe hierarchies*. ᴜʀʟ:https://pigworker.wordpress.com/2015/01/09/universe-hierarchies/.

[2] Oury, Nicolas. (TPHOLs 2005). *Extensionality in the Calculus of Constructions*. ᴅᴏɪ:10.1007/11541868_18.

While it’s rather difficult to accidentally prove an inconsistency in a well-meaning type theory that isn’t obviously inconsistent
(have you ever unintentionally proven that a type corresponding to an ordinal is strictly larger than itself? I didn’t think so),
it feels like it’s comparatively easy to add rather innocent features to your type theory that will suddenly make it inconsistent.
And there are so many of them!
And sometimes it’s the *interaction* among the features rather than the features themselves that produce inconsistencies.

As it turns out, a lot of the inconsistencies can surface as proofs of what’s known as Hurkens’ paradox [1], which is a simplification of Girard’s paradox [2], which itself is a type-theoretical formulation of the set-theoretical Burali–Forti’s paradox [3]. I won’t claim to deeply understand how any of these paradoxes work, but I’ll present various formulations of Hurkens’ paradox in the context of the most well-known inconsistent features.

The most common mechanization of Hurkens’ paradox you can find online is using type-in-type,
where the type of the universe `Type`

has `Type`

itself as its type,
because most proof assistants have ways of turning this check off.
We begin with what Hurkens calls a *powerful paradoxical universe*,
which is a type `U`

along with two functions `τ : ℘ (℘ U) → U`

and `σ : U → ℘ (℘ U)`

.
Conceptually, `℘ X`

is the powerset of `X`

, implemented as `X → Type`

;
`τ`

and `σ`

then form an isomorphism between `U`

and the powerset of its powerset, which is an inconsistency.
Hurkens defines `U`

, `τ`

, and `σ`

as follows, mechanized in Agda below.

```
U : Set
U = ∀ (X : Set) → (℘ (℘ X) → X) → ℘ (℘ X)
τ : ℘ (℘ U) → U
τ t = λ X f p → t (λ x → p (f (x X f)))
σ : U → ℘ (℘ U)
σ s = s U τ
```

The complete proof can be found at Hurkens.html, but we’ll focus on just these definitions for the remainder of this post.

Hurkens’ original construction of the paradox was done in System U⁻, where there are *two* impredicative universes,
there named `*`

and `□`

.
We’ll call ours `Set`

and `Set₁`

, with the following typing rules for function types featuring impredicativity.

```
Γ ⊢ A : 𝒰
Γ, x: A ⊢ B : Set
────────────────── Π-Set
Γ ⊢ Πx: A. B : Set
Γ ⊢ A : 𝒰
Γ, x: A ⊢ B : Set₁
─────────────────── Π-Set₁
Γ ⊢ Πx: A. B : Set₁
```

Going back to the type-in-type proof, consider now `℘ (℘ X)`

.
By definition, this is `(X → Set) → Set`

; since `Set : Set₁`

, by Π-Set₁,
the term has type `Set₁`

, regardless of what the type of `X`

is.
Then `U = ∀ X → (℘ (℘ X) → X) → ℘ (℘ X)`

has type `Set₁`

as well.
Because later when defining `σ : U → ℘ (℘ U)`

, given a term `s : U`

, we want to apply it to `U`

,
the type of `X`

should have the same type as `U`

for `σ`

to type check.
The remainder of the proof of inconsistency is unchanged, as it doesn’t involve any explicit universes,
although we also have the possibility of lowering the return type of `℘`

.
An impredicative `Set₁`

above a predicative `Set`

may be inconsistent as well,
since we never make use of the impredicativity of `Set`

itself.

```
℘ : ∀ {ℓ} → Set ℓ → Set₁
℘ {ℓ} S = S → Set
U : Set₁
U = ∀ (X : Set₁) → (℘ (℘ X) → X) → ℘ (℘ X)
```

Note well that having two impredicative universe layers is *not* the same thing as having two parallel impredicative universes.
For example, by turning on `-impredicative-set`

in Coq, we’d have an impredicative `Prop`

and an impredicative `Set`

,
but they are in a sense parallel universes: the type of `Prop`

is `Type`

, not `Set`

.
The proof wouldn’t go through in this case, since it relies on the type of the return type of `℘`

being impredicative as well.
With cumulativity, `Prop`

is a subtype of `Set`

, but this has no influence for our puposes.

A *strong (dependent) pair* is a pair from which we can project its components.
An *impredicative pair* in some impredicative universe `𝒰`

is a pair that lives in `𝒰`

when either of its components live in `𝒰`

,
regardless of the universe of the other component.
It doesn’t matter too much which specific universe is impredicative as long as we can refer to both it and its type,
so we’ll suppose for this section that `Set`

is impredicative.
The typing rules for the strong impredicative pair are then as follows;
we only need to allow the first component of the pair to live in any universe.

```
Γ ⊢ A : 𝒰
Γ, x: A ⊢ B : Set
──────────────────
Γ ⊢ Σx: A. B : Set
Γ ⊢ a : A
Γ ⊢ b : B[x ↦ a]
─────────────────────
Γ ⊢ (a, b) : Σx: A. B
Γ ⊢ p : Σx: A. B
────────────────
Γ ⊢ fst p : A
Γ ⊢ p : Σx: A. B
────────────────────────
Γ ⊢ snd p : B[x ↦ fst p]
Γ ⊢ (a, b) : Σx: A. B
──────────────────────
Γ ⊢ fst (a, b) ≡ a : A
Γ ⊢ (a, b) : Σx: A. B
─────────────────────────────
Γ ⊢ snd (a, b) ≡ b : B[x ↦ a]
```

If we turn type-in-type off in the previous example, the first place where type checking fails is for `U`

,
which with predicative universes we would expect to have type `Set₁`

.
The idea, then, is to squeeze `U`

into the lower universe `Set`

using the impredicativity of the pair,
then to extract the element of `U`

as needed using the strongness of the pair.
Notice that we don’t actually need the second component of the pair, which we can trivially fill in with `⊤`

.
This means we could instead simply use the following record type in Agda.

```
record Lower (A : Set₁) : Set where
constructor lower
field raise : A
```

The type `Lower A`

is equivalent to `Σx: A. ⊤`

, its constructor `lower a`

is equivalent to `(a, tt)`

,
and the projection `raise`

is equivalent to `fst`

.
To allow type checking this definition, we need to again turn on type-in-type, despite never actually exploiting it.
If we really want to make sure we really never make use of type-in-type,
we can postulate `Lower`

, `lower`

, and `raise`

, and use rewrite rules to recover the computational behaviour of the projection.

```
{-# OPTIONS --rewriting #-}
postulate
Lower : (A : Set₁) → Set
lower : ∀ {A} → A → Lower A
raise : ∀ {A} → Lower A → A
beta : ∀ {A} {a : A} → raise (lower a) ≡ a
{-# REWRITE beta #-}
```

Refactoring the existing proof is straightforward:
any time an element of `U`

is used, it must first be raised back to its original universe,
and any time an element of `U`

is produced, it must be lowered down to the desired universe.

```
U : Set
U = Lower (∀ (X : Set) → (℘ (℘ X) → X) → ℘ (℘ X))
τ : ℘ (℘ U) → U
τ t = lower (λ X f p → t (λ x → p (f (raise x X f))))
σ : U → ℘ (℘ U)
σ s = raise s U τ
```

Again, the complete proof can be found at HurkensLower.html.
One final thing to note is that impredicativity (with respect to function types) of `Set`

isn’t used either;
all of this code type checks in Agda, whose universe `Set`

is not impredicative.
This means that impredicativity with respect to strong pair types alone is sufficient for inconsistency.

In contrast to strong pairs, weak (impredicative) pairs don’t have first and second projections.
Instead, to use a pair, one binds its components in the body of some expression
(continuing our use of an impredicative `Set`

).

```
Γ ⊢ p : Σx: A. B
Γ, x: A, y: B ⊢ e : C
Γ ⊢ C : Set
────────────────────────────
Γ ⊢ let (x, y) := p in e : C
```

The key difference is that the type of the expression must live in `Set`

, and not in any arbitrary universe.
Therefore, we can’t generally define our own first projection function, since `A`

might not live in `Set`

.

Weak impredicative pairs can be generalized to inductive types in an impredicative universe,
where the restriction becomes disallowing arbitrary *large elimination* to retain consistency.
This appears in the typing rule for case expressions on inductives.

```
Γ ⊢ t : I p… a…
Γ ⊢ I p… : (y: u)… → 𝒰
Γ, y: u, …, x: I p… a… ⊢ P : 𝒰'
elim(𝒰, 𝒰') holds
< other premises omitted >
───────────────────────────────────────────────────────────────
Γ ⊢ case t return λy…. λx. P of [c x… ⇒ e]… : P[y… ↦ a…][x ↦ t]
```

The side condition `elim(𝒰, 𝒰')`

holds if:

`𝒰 = Set₁`

or higher; or`𝒰 = 𝒰' = Set`

; or`𝒰 = Set`

and- Its constructors’ arguments are either forced or have types living in
`Set`

; and - The fully-applied constructors have orthogonal types; and
- Recursive appearances of the inductive type in the constructors’ types are syntactically guarded.

- Its constructors’ arguments are either forced or have types living in

The three conditions of the final case come from the rules for definitionally proof-irrelevant `Prop`

[4];
the conditions that Coq uses are that the case target’s inductive type must be a singleton or empty,
which is a subset of those three conditions.
As the pair constructor contains a non-forced, potentially non-`Set`

argument in the first component,
impredicative pairs can only be eliminated to terms whose types are in `Set`

,
which is exactly what characterizes the weak impredicative pair.
On the other hand, allowing unrestricted large elimination lets us define not only strong impredicative pairs,
but also `Lower`

(and the projection `raise`

), both as inductive types.

While impredicative functions can Church-encode weak impredicative pairs, they can’t encode strong ones.

```
Σx: A. B ≝ (P : Set) → ((x : A) → B → P) → P
```

If `Set`

is impredicative then the pair type itself lives in `Set`

,
but if `A`

lives in a larger universe, then it can’t be projected out of the pair,
which requires setting `P`

as `A`

.

There’s a variety of other features that yield inconsistencies in other ways, some of them resembling the set-theoretical Russell’s paradox.

A negative inductive type is one where the inductive type appears to the left of an odd number of arrows in a constructor’s type. For instance, the following definition will allow us to derive an inconsistency.

```
record Bad : Set where
constructor mkBad
field bad : Bad → ⊥
open Bad
```

The field of a `Bad`

essentially contains a negation of `Bad`

itself (and I believe this is why this is considered a “negative” type).
So when given a `Bad`

, applying it to its own field, we obtain its negation.

```
notBad : Bad → ⊥
notBad b = b.bad b
```

Then from the negation of `Bad`

we construct a `Bad`

, which we apply to its negation to obtain an inconsistency.

```
bottom : ⊥
bottom = notBad (mkBad notBad)
```

*This section is adapted from Why must inductive types be strictly positive?*.

A positive inductive type is one where the inductive type appears to the left of an even number of arrows in a constructor’s type.
(Two negatives cancel out to make a positive, I suppose.)
If it’s restricted to appear to the left of *no* arrows (0 is an even number), it’s a *strictly* positive inductive type.
Strict positivity is the usual condition imposed on all inductive types in Coq.
If instead we allow positive inductive types in general, when combined with an impredicative universe (we’ll use `Set`

again),
we can define an inconsistency corresponding to Russell’s paradox.

```
{-# NO_POSITIVITY_CHECK #-}
record Bad : Set₁ where
constructor mkBad
field bad : ℘ (℘ Bad)
```

From this definition, we can prove an injection from `℘ Bad`

to `Bad`

via an injection from `℘ Bad`

to `℘ (℘ Bad)`

defined as a partially-applied equality type.

```
f : ℘ Bad → Bad
f p = mkBad (_≡ p)
fInj : ∀ {p q} → f p ≡ f q → p ≡ q
fInj {p} fp≡fq = subst (λ p≡ → p≡ p) (badInj fp≡fq) refl
where
badInj : ∀ {a b} → mkBad a ≡ mkBad b → a ≡ b
badInj refl = refl
```

Evidently an injection from a powerset of some `X`

to `X`

itself should be an inconsistency.
However, it doesn’t appear to be provable without using some sort of impredicativity.
(We’ll see.)
Coquand and Paulin [5] use the following definitions in their proof, which does not type check without type-in-type,
since `℘ Bad`

otherwise does not live in `Set`

.
In this case, weak impredicative pairs would suffice, since the remaining definitions can all live in the same impredicative universe.

```
P₀ : ℘ Bad
P₀ x = Σ[ P ∈ ℘ Bad ] x ≡ f P × ¬ (P x)
x₀ : Bad
x₀ = f P₀
```

From here, we can prove `P₀ x₀ ↔ ¬ P₀ x₀`

. The rest of the proof can be found at Positivity.html.

Another type-theoretic encoding of Russell’s paradox is Berardi’s paradox [6]. It begins with a retraction, which looks like half an isomorphism.

```
record _◁_ {ℓ} (A B : Set ℓ) : Set ℓ where
constructor _,_,_
field
ϕ : A → B
ψ : B → A
retract : ψ ∘ ϕ ≡ id
open _◁_
```

We can easily prove `A ⊲ B → A ⊲ B`

by identity.
If we postulate the axiom of choice, then we can push the universal quantification over `A ⊲ B`

into the existential quantification of `A ⊲ B`

,
yielding a `ϕ`

and a `ψ`

such that `ψ ∘ ϕ ≡ id`

only when given some proof of `A ⊲ B`

.
However, a retraction of powersets can be stipulated out of thin air using only the axiom of excluded middle.

```
record _◁′_ {ℓ} (A B : Set ℓ) : Set ℓ where
constructor _,_,_
field
ϕ : A → B
ψ : B → A
retract : A ◁ B → ψ ∘ ϕ ≡ id
open _◁′_
postulate
EM : ∀ {ℓ} (A : Set ℓ) → A ⊎ (¬ A)
t : ∀ {ℓ} (A B : Set ℓ) → ℘ A ◁′ ℘ B
t A B with EM (℘ A ◁ ℘ B)
... | inj₁ ℘A◁℘B =
let ϕ , ψ , retract = ℘A◁℘B
in ϕ , ψ , λ _ → retract
... | inj₂ ¬℘A◁℘B =
(λ _ _ → ⊥) , (λ _ _ → ⊥) , λ ℘A◁℘B → ⊥-elim (¬℘A◁℘B ℘A◁℘B)
```

This time defining `U`

to be `∀ X → ℘ X`

, we can show that `℘ U`

is a retract of `U`

.
Here, we need an impredicative `Set`

so that `U`

can also live in `Set`

and so that `U`

quantifies over itself as well.
Note that we project the equality out of the record while the record is impredicative,
so putting `_≡_`

in `Set`

as well will help us avoid large eliminations for now.

```
projᵤ : U → ℘ U
projᵤ u = u U
injᵤ : ℘ U → U
injᵤ f X =
let _ , ψ , _ = t X U
ϕ , _ , _ = t U U
in ψ (ϕ f)
projᵤ∘injᵤ : projᵤ ∘ injᵤ ≡ id
projᵤ∘injᵤ = retract (t U U) (id , id , refl)
```

Now onto Russell’s paradox.
Defining `_∈_`

to be `projᵤ`

and letting `r ≝ injᵤ (λ u → ¬ u ∈ u)`

,
we can show a curious inconsistent statement.

```
r∈r≡r∉r : r ∈ r ≡ (¬ r ∈ r)
r∈r≡r∉r = cong (λ f → f (λ u → ¬ u ∈ u) r) projᵤ∘injᵤ
```

To actually derive an inconsistency, we can derive functions `r ∈ r → (¬ r ∈ r)`

and `(¬ r ∈ r) → r ∈ r`

using substitution,
then prove falsehood the same way we did for negative inductive types.
However, the predicate in the substitution is `Set → Set`

, which itself has type `Set₁`

,
so these final steps do require unrestricted large elimination.
The complete proof can be found at Berardi.html.

Having impredicative inductive types that can be eliminated to large types can yield an inconsistency
without having to go through Hurkens’ paradox.
To me, at least, this inconsistency is a lot more comprehensible.
This time, we use an impredicative representation of the ordinals [7],
prove that they are well-founded with respect to some reasonable order on them,
then prove a falsehood by providing an ordinal that is obviously *not* well-founded.
This representation can be type checked using Agda’s `NO_UNIVERSE_CHECK`

pragma,
and normally it would live in `Set₁`

due to one constructor argument type living in `Set₁`

.

```
{-# NO_UNIVERSE_CHECK #-}
data Ord : Set where
↑_ : Ord → Ord
⊔_ : {A : Set} → (A → Ord) → Ord
data _≤_ : Ord → Ord → Set where
↑s≤↑s : ∀ {r s} → r ≤ s → ↑ r ≤ ↑ s
s≤⊔f : ∀ {A} {s} f (a : A) → s ≤ f a → s ≤ ⊔ f
⊔f≤s : ∀ {A} {s} f → (∀ (a : A) → f a ≤ s) → ⊔ f ≤ s
```

An ordinal is either a successor ordinal, or a limit ordinal.
(The zero ordinal could be defined as a limit ordinal.)
Intuitively, a limit ordinal `⊔ f`

is the supremum of all the ordinals returned by `f`

.
This is demonstrated by the last two constructors of the preorder on ordinals:
`s≤⊔f`

states that `⊔ f`

is an upper bound of all the ordinals of `f`

,
while `⊔f≤s`

states that it is the *least* upper bound.
Finally, `↑s≤↑s`

is simply the monotonicity of taking the successor of an ordinal with respect to the preorder.
It’s possible to show that `≤`

is indeed a preorder by proving its reflexivity and transitivity.

```
s≤s : ∀ {s : Ord} → s ≤ s
s≤s≤s : ∀ {r s t : Ord} → r ≤ s → s ≤ t → r ≤ t
```

From the preorder we define a corresponding strict order.

```
_<_ : Ord → Ord → Set
r < s = ↑ r ≤ s
```

In a moment, we’ll see that `<`

can be proven to be *wellfounded*,
which is equivalent to saying that in that there are no infinite descending chains.
Obviously, for there to be no such chains, `<`

must at minimum be irreflexive — but it’s not!
There is an ordinal that is strictly less than itself,
which we’ll call the “infinite” ordinal,
defined as the limit ordinal of *all* ordinals,
which is possible due to the impredicativity of `Ord`

.

```
∞ : Ord
∞ = ⊔ (λ s → s)
∞<∞ : ∞ < ∞
∞<∞ = s≤⊔f (λ s → s) (↑ ∞) s≤s
```

To show wellfoundedness, we use an *accessibility predicate*,
whose construction for some ordinal `s`

relies on showing that all smaller ordinals are also accessible.
Finally, wellfoundness is defined as a proof that *all* ordinals are accessible,
using a lemma to extract accessibility of all smaller or equal ordinals.

```
record Acc (s : Ord) : Set where
inductive
pattern
constructor acc
field
acc< : (∀ r → r < s → Acc r)
accessible : ∀ (s : Ord) → Acc s
accessible (↑ s) = acc (λ { r (↑s≤↑s r≤s) → acc (λ t t<r → (accessible s).acc< t (s≤s≤s t<r r≤s)) })
accessible (⊔ f) = acc (λ { r (s≤⊔f f a r<fa) → (accessible (f a)).acc< r r<fa })
```

But wait, we needed impredicativity *and* large elimination.
Where is the large elimination?

It turns out that it’s hidden within Agda’s pattern-matching mechanism.
Notice that in the limit case of `accessible`

, we only need to handle the `s≤⊔f`

case,
since this is the only case that could possibly apply when the left side is a successor and the right is an ordinal.
However, if you were to write this in plain CIC for instance,
you’d need to first explicitly show that the order could not be either of the other two constructors,
requiring showing that the successor and limit ordinals are provably distinct
(which itself needs large elimination, although this is permissible as an axiom),
then due to the proof architecture show that if two limit ordinals are equal, then their components are equal.
This is known as *injectivity of constructors*.
Expressing this property for ordinals requires large elimination,
since the first (implicit) argument of limit ordinals are in `Set`

.

You can see how it works explicitly by writing the same proof in Coq,
where the above steps correspond to inversion followed by dependent destruction,
then printing out the full term.
The `s≤⊔f`

subcase of the `⊔ f`

case alone spans 50 lines!

In any case, we proceed to actually deriving the inconsistency, which is easy:
show that `∞`

is in fact *not* accessible using `∞<∞`

,
then derive falsehood directly.

```
¬accessible∞ : Acc ∞ → ⊥
¬accessible∞ (acc p) = ¬accessible∞ (p ∞ ∞<∞)
ng : ⊥
ng = ¬accessible∞ (accessible ∞)
```

The complete Agda proof can be found at Ordinals.html, while a partial Coq proof of accessibility of ordinals can be found at Ordinals.html.

The recursive calls of cofixpoints must be *guarded by constructors*,
meaning that they only appear as arguments to constructors of the coinductive type of the cofixpoint.
What’s more, the constructor argument type must be syntactically the coinductive type,
not merely a polymorphic type that’s filled in to be the coinductive type later.
If the guardedness condition is relaxed to ignore this second condition,
then we can derive an inconsistency with impredicative coinductives.
Again, we define one using `NO_UNIVERSE_CHECK`

, with contradictory fields.
Evidently we should never be able to construct such a coinductive.

```
{-# NO_UNIVERSE_CHECK #-}
record Contra : Set where
coinductive
constructor contra
field
A : Set
a : A
¬a : A → ⊥
¬c : Contra → ⊥
¬c = λ c → (¬a c) (a c)
```

However, if the type field in `Contra`

is `Contra`

itself,
then we can in fact construct one coinductively.
Here, we use `NON_TERMINATING`

to circumvent Agda’s perfectly correct guardedness checker,
but notice that the recursive call is inside of `contra`

and therefore still “guarded”.
This easily leads to an inconsistency.

```
{-# NON_TERMINATING #-}
c : Contra
c = contra Contra c ¬c
ng : ⊥
ng = ¬c c
```

This counterexample is due to Giménez [8] and the complete proof can be found at Coind.html.

The combinations of features that yield inconsistencies are:

- Type-in-type:
`· ⊢ Set : Set`

- Impredicative
`Set`

and`Set₁`

where`· ⊢ Set : Set₁`

- Strong impredicative pairs
- Impredicative inductive types + unrestricted large elimination
- Negative inductive types
- Non-strictly-positive inductive types + impredicativity
- Impredicativity + excluded middle + unrestricted large elimination
- Impredicative inductive types + unrestricted large elimination (again)
- Relaxed guardedness of cofixpoints

`Lower`

: HurkensLower.html[1] Hurkens, Antonius J. C. (TLCA 1995). *A Simplification of Girard’s Paradox*. ᴅᴏɪ:10.1007/BFb0014058.

[2] Coquand, Thierry. (INRIA 1986). *An Analysis of Girard’s Paradox*. https://hal.inria.fr/inria-00076023.

[3] Burali–Forti, Cesare. (RCMP 1897). *Una questione sui numeri transfini*. ᴅᴏɪ:10.1007/BF03015911.

[4] Gilbert, Gaëtan; Cockx, Jesper; Sozeau, Matthieu; Tabareau, Nicolas. (POPL 2019). *Definitional Proof-Irrelevance without K*. ᴅᴏɪ:10.1145/3290316.

[5] Coquand, Theirry; Paulin, Christine. (COLOG 1988). *Inductively defined types*. ᴅᴏɪ:10.1007/3-540-52335-9_47.

[6] Barbanera, Franco; Berardi, Stefano. (JFP 1996). *Proof-irrelevance out of excluded middle and choice in the calculus of constructions*. ᴅᴏɪ:10.1017/S0956796800001829.

[7] Pfenning, Frank; Christine, Paulin-Mohring. (MFPS 1989). *Inductively defined types in the Calculus of Constructions*. ᴅᴏɪ:10.1007/BFb0040259.

[8] Giménez, Eduardo. (TYPES 1994). *Codifying guarded definitions with recursive schemes*. ᴅᴏɪ:10.1007/3-540-60579-7_3.

It was a miracle I ended up mostly physically unscathed. I instinctively shot out my arms underneath me and I could have broken a wrist or two. I could’ve landed on my tailbone wrong and broken that too. But it seemed the only damage was a light bruise on my sacrum (had to look up the word for that) and to my ego. Not even a scratch on my palms! Then I sat down, took out my laptop, opened it, and discovered the large crack from the bottom left corner, reaching longingly to conquer the entirety of the screen.

This step is really important.
If you don’t wallow in the despair now, when you’re powerless on the bus,
the despair will overtake you when you actually want to take action.
Allow it to pass over and through you.^{1}
Now we are ready to fix the screen.

I remembered a friend in my research lab had also replaced their ThinkPad screen, who got their replacement from Laptop Screen. Searching for my model, I found the appropriate replacement; unfortunately I couldn’t upgrade from FHD to QHD, because the former uses a 30-pin connected while the latter 40-pin, but I could upgrade to an IPS screen with “professional colour”, if that even makes a noticeable difference.

Even though I could’ve transited to their warehouse in under an hour and a half,
there wasn’t a pickup option, so I had to pay 9.86$ for shipping and 2.53$ for insurance.
Then with GST and PST, the total came to **163.22$**.
High price for a slipping accident.

I ordered it on Friday and they managed to get it shipped the same day, but since Canada Post doesn’t deliver on weekends, I had to wait out the weekend. I spent this time setting up my brother’s monitor temporarily and making the crack worse because I couldn’t resist.

In other news the laptop screen is not doing great. idk if you can see the flickering but this has gone from "I can use ¾ of the screen I guess" to "no ❤️" pic.twitter.com/1XBET9qeem

— 🥢 {#- OPTIONS --sized-types -#} 🎃 (@ionathanch) October 23, 2021

Now it’s time for the fun part: replacing the screen. Before I started, I watched through this tutorial, which was pretty close to how my model (the ThinkPad T480s) works. I first peeled off the soft plastic bezel that was attached on with long, black adhesive strips. I didn’t have a spudger, so I did accidentally scratch one part of the outside of the bezel with my flathead. Some of the adhesive strips were left stuck to the screen, so I had to peel them off and stick them back on the bezel.

Next part is easy: Remove the four screws holding the screen in place and unplug the 30-pin connector. There’s this thin metal latch that folds down to secure the plug; it has to be flipped up first. I didn’t notice it at first, because I wasn’t expecting it from what I saw in the tutorial, and I was nearly about to rip the cable in half.

Now I can plug in the new screen, flip the latch down, turn on my computer briefly to test the screen, screw the new screen in firmly, peel off the protective plastic, and finally put the bezel back. This was probably the most difficult step and took me three tries to get it aligned correctly. And it’s done! The screen looks brand new (because it is), and you can barely tell where I scratched the bezel.

Preferrably one with very padded sides to prevent similar drop damage. I don’t want to have to spend another 163.22$ for my mistakes.

^{1}
Excuse the reference, Dune (2021) with Paul played by renowned twink Timothée Chalamet just came out a few days ago.

*This post is inspired by this thread
from the Agda mailing list.*

Because Agda treats `Size`

like a first-class type,
there’s quite a bit of freedom in how you get to use it.
This makes it a bit unclear where you should start:
should `Size`

be a parameter or an index in your type?
What sizes should the constructors’ recursive arguments’ and return types have?
Here are two options that are usually used (and one that doesn’t work.)
The recommended one is using inflationary sized types and `Size<`

.

We’ll be looking at adding sized types to a mutually-defined tree/forest inductive type.
A tree parametrized by `A`

contains an `A`

and a forest,
while a forest consists of a bunch of trees.

```
{-# OPTIONS --sized-types #-}
open import Size
variable
A : Set
module Forestry where
data Tree {A : Set} : Set
data Forest {A : Set} : Set
data Tree {A} where
node : A → Forest {A} → Tree
data Forest {A} where
leaf : Forest
cons : Tree {A} → Forest {A} → Forest
traverse : (Tree {A} → Tree {A}) → Forest {A} → Forest {A}
traverse f leaf = leaf
-- traverse f (cons (node a forest) rest) = cons (f (node a (traverse f forest))) (traverse f rest)
traverse f (cons tree rest) with f tree
... | node a forest = cons (node a (traverse f forest)) (traverse f rest)
```

We can traverse a forest with a function that acts on trees. The first (commented out) is to first traverse the forest contained within a tree, then apply the function to the tree, which is structurally guarded. The second is to traverse the forest of the tree applied to the function, which is not. However, if we promise to not alter the size of the tree with our function—that is, using a size-preserving function—then we’ll be able to implement this post-traversal.

Here’s a recipe for turning your inductives into sized inductives.

- In the types of the inductives, add
`Size`

as an index. - For every constructor, add a size argument
`∀ s`

. - For every recursive constructor argument (that includes the mutual ones), give it size
`s`

. - For every constructor return type, give it size
`↑ s`

.

I call this pattern *successor* sized types since every constructible inductive’s size is a successor size.
Then it will always have a larger size than any of its recursive arguments.
I tend to keep the sizes in the inductives’ types explicit,
so that size-preserving function signatures are clear,
but make the sizes in the constructors’ types implicit,
since those can usually be inferred when the sizes are present in the function signatures.
Applied to trees and forests, this is what we get.

```
module SuccSizedForestry where
data Tree {A : Set} : Size → Set
data Forest {A : Set} : Size → Set
data Tree {A} where
node : ∀ {s} → A → Forest {A} s → Tree (↑ s)
data Forest {A} where
leaf : ∀ {s} → Forest (↑ s)
cons : ∀ {s} → Tree {A} s → Forest {A} s → Forest (↑ s)
```

The traversal function is easy: just add the sizes in the type, and type checking will do the rest.

```
traverse : ∀ {s} → (∀ {r} → Tree {A} r → Tree {A} r) → Forest {A} s → Forest {A} s
traverse f leaf = leaf
traverse f (cons tree rest) with f tree
... | node a forest = cons (node a (traverse f forest)) (traverse f rest)
```

There’s one strange thing with our current definition:
in `cons`

, the tree and the forest need to have the same size.
Given that they’re not only two different arguments but also two arguments of different types,
why should they have the same size?
When constructing a forest, you’ll certainly run into the issue of
needing to bump up the size of one or the other in order to use `cons`

.
Let’s see if we can avoid this by using different sizes.

Spoiler alert: this doesn’t pass termination checking in Agda… but let’s proceed anyway. The recipe is modified slightly.

- In the types of the inductives, add
`Size`

as an index. - For every constructor, add a size argument
`∀ sᵢ`

for each of the recursive arguments. - For each
`i`

th recursive constructor argument, give it size`sᵢ`

. - For every constructor return type with
`n`

recursive arguments, you have two options:- Give it size
`↑ (s₁ ⊔ˢ s₂ ⊔ˢ ... ⊔ˢ sₙ)`

; or - Give it size
`(↑ s₁) ⊔ˢ (↑ s₂) ⊔ˢ ... ⊔ˢ (↑ sₙ)`

.

- Give it size

The supremum operator `s₁ ⊔ˢ s₂`

can be thought of as taking the maximum of the two sizes.
The idea is that if you have two arguments of size `s₁`

and `s₂`

,
the size of the constructed term should have a size larger than whichever is largest,
and `↑ (s₁ ⊔ˢ s₂)`

and `(↑ s₁) ⊔ˢ (↑ s₂)`

are equivalent.
I’ve chosen the former below.

```
module SupSizedForestry where
data Tree {A : Set} : Size → Set
data Forest {A : Set} : Size → Set
data Tree {A} where
node : ∀ {s} → A → Forest {A} s → Tree (↑ s)
data Forest {A} where
leaf : ∀ {s} → Forest (↑ s)
cons : ∀ {st sf} → Tree {A} st → Forest {A} sf → Forest (↑ (st ⊔ˢ sf))
-- cons : ∀ {st sf} → Tree {A} st → Forest {A} sf → Forest ((↑ st) ⊔ˢ (↑ sf))
```

However, the traversal doesn’t pass termination checking. I’ve specified some of the sizes explicitly for clarity.

```
traverse : ∀ {s} → (∀ {r} → Tree {A} r → Tree {A} r) → Forest {A} s → Forest {A} s
traverse f leaf = leaf
traverse f (cons tree forest) with f tree
traverse {_} .{↑ ((↑ st) ⊔ˢ sf)} f (cons .{↑ st} {sf} tree forest)
| (node {st} a forest') = cons (node a (traverse {_} {st} f forest')) (traverse {_} {sf} f forest)
```

It appears that Agda can’t deduce that `st`

and `sf`

are both strictly smaller than `↑ ((↑ st) ⊔ˢ sf)`

.
(To see that this is true, if `sf > ↑ st`

, then `↑ ((↑ st) ⊔ˢ sf) = ↑ sf > sf > ↑ st`

;
otherwise, `↑ ((↑ st) ⊔ˢ sf) = ↑ ↑ st > ↑ st ≥ sf`

.)

The last option does work in Agda, and has the benefit of allowing different sizes for different arguments. Overall, this is the ideal option to choose. The first option is more of a historical artifact than anything. The recipe is a little different:

- In the types of the inductives, add
`(s : Size)`

as a*parameter*. - For every constructor, add a size argument
`∀ (rᵢ : Size< s)`

for each of the recursive arguments. - For each
`i`

th recursive constructor argument, give it size`rᵢ`

. - The return types of the constructors necessarily have size
`s`

, since it’s a parameter.

The `Size< : Size → Set`

type constructor lets us declare a size strictly smaller than a given size.
Then just as in the first option, every recursive argument must have a smaller size.
These are called inflationary sized types because they correspond to inflationary fixed points in the metatheory,
but I prefer to simply think of them as *bounded* sized types.

```
module BoundedSizedForestry where
data Tree {A : Set} (s : Size) : Set
data Forest {A : Set} (s : Size) : Set
data Tree {A} s where
node : ∀ {r : Size< s} → A → Forest {A} r → Tree s
data Forest {A} s where
leaf : ∀ {r : Size< s} → Forest s
cons : ∀ {st sf : Size< s} → Tree {A} st → Forest {A} sf → Forest s
```

Then the traversal is exactly the same as in the first option, and passes termination checking without any further effort.

```
traverse : ∀ {s} → (∀ {r} → Tree {A} r → Tree {A} r) → Forest {A} s → Forest {A} s
traverse f leaf = leaf
traverse f (cons tree rest) with f tree
... | node a forest = cons (node a (traverse f forest)) (traverse f rest)
```

*This example is lifted from issue #2820 in the Agda GitHub repository.*

Agda’s sized types comes with an infinite size `∞`

that you can sprinkle in anywhere.
This is handy for specifying sized arguments whose size you don’t care about
(e.g. if you’re not recurring on them),
and for specifying sized return types that are “too big” to be expressible as a finite size,
such as the return type of a factorial function.
In order for `∞`

to behave as you’d expect, it needs to satisfy `∞ + 1 = ∞`

,
which implies `∞ + 1 < ∞`

.
However, we can also show that the order `<`

on sizes is well-founded,
thus yielding a contradiction in the presence of `∞`

.

Agda already has an order on sizes via `Size<`

, but this is hard to manipulate.
We can instead define an inductive type that reflects this order.

```
module False where
open import Data.Empty
data _<_ : Size → Size → Set where
lt : ∀ s → (r : Size< s) → r < s
```

Next, we define accessibility with respect to this order, which states that for some size `s`

,
if every smaller size is accessible, then `s`

itself is accessible.
Agda’s standard library has an accessibility relation parametrized over an arbitrary order,
but I’ll redefine it explicitly for sizes for clarity.

```
data Acc (s : Size) : Set where
acc : (∀ {r} → r < s → Acc r) → Acc s
```

Now we can state wellfoundedness of sizes, which is simply that every size is accessible.
If this is true, then surely there should be no infinitely-descending chain `... s₃ < s₂ < s₁`

.

```
wf : ∀ s → Acc s
wf s = acc (λ {(lt .s r) → wf r})
```

This proof appears to rely on the fact that the type of `r`

gets unified with `Size< s`

when matching on `r < s`

.
Then termination checking passes because `wf`

is called on a smaller size.
Conventionally, this kind of proof is structurally-decreasing based on case analysis of the thing that’s accessible,
but we can’t inspect sizes like that in Agda.

The problem with saying that sizes are wellfounded with respect to the size order is that they are not!
We have the infinitely-descending chain `... ∞ < ∞ < ∞`

.
The fact that `∞`

is *not* accessible can be proven by structural induction on the accessibility relation,
without the help of sized termination checking.

```
¬wf∞ : Acc ∞ → ⊥
¬wf∞ (acc p) = ¬wf∞ (p (lt ∞ ∞))
```

Finally, we prove falsehood from this and the contradictory fact that `∞`

is wellfounded
because we’ve just proven that *all* sizes are wellfounded.

```
ng : ⊥
ng = ¬wf∞ (wf ∞)
```