Scarlet Serenade

Types and Programming Languages | Nameless Representation of Terms

We can devise some "canonical" representation of variables and terms that does not require renaming.

De Bruijn's idea was that we can represent terms by making variable occurrences point directly to their binders, rather than referring to them by name. This can be accomplished by replacing named variables by natural numbers, where the number k stands for "the variable bound by the k'th enclosing .

  • e.g., corresponds to the nameless term
  • e.g., corresponds to the nameless term

Nameless terms are also sometimes called de Bruijn terms, and the numeric variables in them are called de Bruijn indices. Compiler writers use the term static distances for the same concept.

Converting to Nameless Representation

We will define a function mapping terms to nameless representation, and to deal with terms containing free variables, we need the idea of a naming context .

  • For example, suppose we want to represent as a nameless term. But we cannot see the binder for y, so it is not clear how "far away" it might be and we do not know what number to assign to it.
  • suppose we have then

-- - would be represented as -- - would be represented as . because of the abstraction of , the distance of is increased by 1, hence 3 + 1 Let's simplify to be a sequence of var names:

Then we'll define and So can be defined as:

The we have


Note that each closed term has just one de Bruijn representation, and two ordinary terms are equivalent modulo renaming of bound vars iff they have the same de Bruijn representation.

  • (没懂,modulo 是啥


We need to keep track of how many free vars each term may contain. That is, we distinguish the sets of terms with no free vars (called the 0-terms), terms with at most one free vars (1-terms), and so on.

Definition: let be the smallest family of sets such that

    1. whenever
    1. if and then
    1. if and then

The elements of are terms with at most free vars, numbered between and .

Substitution on Nameless Terms

When a substitution goes under a λ-abstraction, as in , the context in which the substitution is taking places becomes one var longer than the original, so we need to increment the indices of the free vars inside . We define one auxiliary operation called shifting to do this.

(* 每当 `term` 被代入到一个 abs 的 body 内,都需要将其所有自由变量移动一位 *)
let shift term =
  (* 根据与当前深度判断是否是自由变量 *)
  let rec h depth t =
    match t with
    (* 如果变量的编号大于等于当前深度,说明 its binder is outside of `term` *)
    (* 那么对于 `term` 来说它是一个自由变量,否则不是 *)
    | TmVar n -> TmVar (if n >= depth then n + 1 else n)
    (* 当前深度 + 1 *)
    | TmAbs (arg_name, body) -> TmAbs (arg_name, h (depth + 1) body)
    | TmApp (t1, t2) -> TmApp (h depth t1, h depth t2)
  in
  (* it starts off at 0 (meaning all vars should be shifted) *)
  h 0 term

let rec substitute from to' term =
  match term with
  (* 如果刚好是目标变量,那么替换 *)
  | TmVar n -> if from = n then to' else TmVar n
  (* 因为更深入了一层,所以需要 shift 一次 `to'` *)
  (* 因为更深入了一层,所以目标变量离 its binder 的距离也增加了 1 *)
  | TmAbs (arg_name, body) ->
      TmAbs (arg_name, substitute (from + 1) (shift to') body)
  | TmApp (t1, t2) -> TmApp (substitute from to' t1, substitute from to' t2)

let app (TmApp (TmAbs (_, body), t2)) =
  (* 这里 `t2` 进入了更深的一层,所以需要 `shift` 一次 *)
  (* 当 reducing redex,function body 的深度减小了一,则其中所有的自由变量需要负向移动一位,
     我们可以通过与 `shift` 相同的方式定义一个 `unshift` *)
  unshift (substitute 0 (shift t2) body)