1百万(1M)面向非专业人士:介绍
1ML for non-specialists: introduction

原始链接: https://pithlessly.github.io/1ml-intro

## 揭秘 1ML:一种用于模块化编程的类型系统 本系列旨在以比原始学术论文更易理解的方式解释 Andreas Rossberg 的 1ML 类型系统——被认为是将 ML 风格的模块集成到新语言中的有力候选者。1ML 从根本上来说,是作为对 System Fω 的一种复杂的“语法糖”,System Fω 是带有显式多态性的 lambda 演算的强大扩展。 核心思想是将 1ML 代码翻译成 Fω,然后进行类型检查。一个关键的挑战在于理解“表面”类型(如在 1ML 中编写的类型)与它们内部 Fω 表示形式之间的区别。作者的实现侧重于表示大型语义类型,而将小型类型视为不变式,而不是独立的数据类型。 本系列将深入研究 1ML 的推理规则的复杂性,力求完全理解,并讨论论文中未涵盖的实际实现问题,例如将类型推断与层次集成。它*不会*涵盖形式元理论(健全性证明),但会针对编译器作者,对论文的呈现方式提出批判性观点。未来的条目将涵盖 1ML 语法、去糖和阶段分离。

## 1ML:一种类型系统的新方法 一篇Hacker News讨论围绕着Andreas Rossberg创建的1ML类型系统展开。与“机器学习”不同,这里的“ML”代表“元语言”。1ML独特地将核心语言元素与其模块系统融合,提供了一种更精简的语言设计,并具有与Standard ML (SML)相当的严格类型安全证明。 对话强调了类型系统理论研究与实际编译器开发之间的一种脱节。一些人认为,专注于语言语义的学者不一定需要优先构建编译器,因为编译器需要持续维护。有人建议将目标设为LLVM IR或使用Prolog进行实现。 然而,另一些人强调,即使是一个基本的编译器也是一种沟通工具,并能激发更广泛的采用。一个研究原型解释器确实存在,但尚未达到生产就绪状态。讨论涉及更广泛的问题,即学术研究仍然孤立,以及更注重工程的语言设计方法的好处,以及类型系统在编译器之外的更广泛应用。
相关文章

原文

@pithlessly · github · λ

This is the first entry in what I intend as a multi-part series.

1ML is a type system designed by Andreas Rossberg and described in a collection of papers by him. When it comes to integrating an ML-style module system into a new language, I think 1ML currently provides the best overall story (although it’s not perfect).

Unfortunately, Rossberg’s papers are pretty technical. He is a good writer, but I feel that right now there is still a communication barrier between academics who are in a position to discuss 1ML in depth and people who are in a position to write new compilers. I see a lot more discussion online that merely references 1ML than I do discussion that seems to deeply understand it.

I’ve been studying 1ML for the last several months, and working on my own prototype implementation (which unfortunately isn’t yet in a publishable state). In an effort to help these ideas percolate down towards mainstream adoption, I want to help demystify some things that I found confusing.

This series will not substitute for reading the paper, but I hope it can be a useful companion. I’ll be working from the version of the paper published in the Journal of Functional Programming in 2018. It’ll be very helpful to be able to look at that paper as you read this. In fact, you can press this button to pull it up on the side in this page: (It probably doesn’t look great on mobile, unfortunately.)

Prerequisite knowledge

In writing this, I assume the reader:

  • Has some familiarity with inference rule notation and how type systems are defined using a combination of grammars and inference rules.
  • Has some familiarity with the module system of OCaml or Standard ML, from a user’s perspective (not necessarily knowing their internals).
  • Has familiarity with 1ML’s design goals as outlined in the paper introduction.
  • Has not necessarily read the prequel, F-ing modules, Rossberg’s earlier paper on compiling module calculi to Fω.

Goals

By the end of this series of articles, the reader will have the tools they need to understand every single inference rule, including those of the type inference algorithm described in Section 7.

I also want to discuss some implementation concerns that are not mentioned in the paper (such as how to integrate type inference with levels).

Non-goals

A decent portion of the paper is dedicated to meta-theory (e.g. the proofs that the type system is sound, etc.). I have not read these sections in depth, and will not be discussing them here. It is reasonable for compiler authors to take these results as given and not focus on their details.

In some places, I will be criticizing the way the paper presents its ideas. Please do not take these as prescriptions for how it should have been written. I do not have the standing to make prescriptions like that; the paper was written with considerations other than the ones most important to me (such as brevity of the rules, ease of meta-theory, & an intended audience of other academics).

System Fω and the architecture of the type system

At a basic level, 1ML is really just a sophisticated syntactic sugar for a version of the lambda calculus called System Fω. If you’re not familiar, this is basically the core type system of Haskell, including higher-kinded type constructors, with all polymorphism being explicit. 1ML terms (I’ll use term and expression interchangeably) are converted into Fω terms and 1ML types are converted into Fω types.

The specific structure of this translation is best saved for a later article. For now, I’ll say that whenever a type TT appears in the surface syntax, it is immediately converted to a Fω type written with the variable Ξ\Xi. After this translation, the rules completely forget about TT and don’t touch it again. This is why we see similar constructs written with two different syntaxes, like (= type T)\text{(= \textbf{type} $T$)} and [=τ][= \tau]

This may seem a little unfamiliar; I was used to seeing type systems that don’t distinguish between the surface syntax for types and the internal representation of types. But of course, a routine like this exists in any real-world type checker, it’s just that many papers would consider it “out of scope” and not discuss it.

Technical note: order of record fields

Whenever a type system defines a class of objects CC, it will often come with some invariants (“every cCc \in C

Unfortunately, when it comes to how Fω’s record field labels work, the paper isn’t so clear. Fig. 3, which defines the syntax of Fω, has only this to say:

τ::={l:τ}e::={l=e} \tau ::= \dots \mid \{ \overline{ l : \tau } \} \qquad e ::= \dots \mid \{ \overline{ l = e } \}

(See below for an explanation of the overline notation.)

However, Fig. 4, which provides typing rules, has this rule for typing field access:

Γe:{l:τ,l:τ}Γe.l:τ \frac{ \Gamma \vdash e : \{ l : \tau, \overline{ l' : \tau' } \} }{ \Gamma \vdash e.l : \tau }

This rule only makes sense if we understand record fields to be intrinsically unordered. I think we can also reasonably assume that l\overline{ l }

I want to linger on the type system of Fω for a bit longer. We can think of Fω type expressions τ\tau as terms in a simply-typed lambda calculus whose base “type” is Ω\Omega, the kind of types. This is the core of what gives Fω, and therefore 1ML, its power — type checking can invoke some form of computation at the type level. This power comes with a corresponding burden for the implementer, who must essentially build an interpreter for STLC into their type checker.

The key rule is this one:

Γe:τττΓτ:ΩΓe:τ \frac{ \Gamma \vdash e : \tau' \qquad \tau' \equiv \tau \qquad \Gamma \vdash \tau : \Omega }{ \Gamma \vdash e : \tau }

In words: “whenever an Fω term ee is considered to have a type τ\tau'

The type equivalence rules are presented as two-directional equalities (\equiv), but in practice, a compiler will want to think of these as one-directional reductions. You can use a technique like normalization by evaluation for this. Bringing types all the way to normal form whenever you construct them is a viable approach, but it gets slow for very large types; there are standard techniques available for being lazier (this is not 1ML-specific).

Semantic types and modeling them in code

Figure 6 defines the grammar for semantic types. It’s important, so I’ll reproduce it here.

(abstracted)Ξ::=α.Σ(large)Σ::=πbool[=Ξ]{l:Σ}α.ΣηΞ(small)σ::=πbool[=σ]{l:σ}σIσ(paths)π::=απσ(purity)η::=PI \begin{align*} \text{(abstracted)} \quad && \Xi \quad & ::= \quad \exists \overline\alpha. \Sigma \\ \text{(large)} \quad && \Sigma \quad & ::= \quad \pi \mid \text{bool} \mid [= \Xi] \mid \{ \overline{ l : \Sigma } \} \mid \forall \overline\alpha. \Sigma \to_\eta \Xi \\ \text{(small)} \quad && \sigma \quad & ::= \quad \pi \mid \text{bool} \mid [= \sigma] \mid \{ \overline{ l : \sigma } \} \mid \sigma \to_{\texttt{I}} \sigma \\ \text{(paths)} \quad && \pi \quad & ::= \quad \alpha \mid \pi \overline\sigma \\ \text{(purity)} \quad && \eta \quad & ::= \quad \texttt{P} \mid \texttt{I} \end{align*}

Note: the meaning of overline notation, as in l:Σ\overline{l : \Sigma}

This is meta-theoretic syntax that just means “zero or more” (sometimes including delimiting commas). For those familiar with Rust’s macro system, it is comparable to something like { $($l:label : $sigma:large_typ),* }.

Large and small semantic types

The first thing we should observe is that large and small types aren’t really defining a new grammar, but instead a subgrammar of Fω types (so we have {σ}{Σ}{Ξ}{τ}\{ \sigma \} \subseteq \{ \Sigma \} \subseteq \{ \Xi \} \subseteq \{ \tau \}

The only wrinkle is the new type formers: the singleton [=Ξ][= \Xi]

Paths

The path grammar is a little wonky. It allows repetition of σ\sigma in two different ways, suggesting that a path is an Fω type variable applied to a list of lists of small types, like π::=ασ\pi ::= \alpha \overline{\overline\sigma}

I honestly find the whole idea of a path a little poorly motivated. My best attempt to articulate the role paths play in the rest of the paper is that they enforce that a semantic type is (roughly) in normal form (since type-level function applications always have a variable at their head), and that only small types can be applied as arguments to functions. “Path” is really a misnomer from this perspective. (The fact that a set of paths is an input to the subtyping relation ΓΣπΣ\Gamma \vdash \Sigma' \leq_{\overline\pi} \Sigma

Abstracted types

The abstracted type grammar Ξ::=α.Σ\Xi ::= \exists \overline\alpha. \Sigma You should think of “abstracted types” as corresponding to signatures (a.k.a. module types) in traditional descriptions of ML. They can contain one or more abstract types (the α\overline\alpha

As an illustration of this, you can look at the desugaring rules on page 25, which say that the surface signature { type t; x : t } gets desugared to Ξ=α:Ω.{t:[=α],x:α}\Xi = \exists \alpha : \Omega. \{ t : [= \alpha], x : \alpha \}

In my opinion, writing \exists here is an abuse of notation. A better mental model of an abstracted type is something like (α:κ.Σ)(\overline{\alpha : \kappa}. \Sigma)

A summary of my implementation recommendations

Wrapping up

That’s all I have time for today. In the next entry, I’ll cover the surface syntax of 1ML, the desugaring judgement ΓTΞ\Gamma \vdash T \leadsto \Xi

Footnotes

联系我们 contact @ memedata.com