The Little Typer

目前的排版不论从效果还是从实现来看都相当丑陋, 但也只能将就一下了. 或许只有等到博客进行大型实质性重构时, 我才会考虑这些问题.

和其他小人书一样, 本书也包含大量文字游戏, 这有点难以传达? 若有必要, 我会添加一些译注.

他序

自序

第1章 愈是变化, 愈是不变

欢迎回来!1很高兴回到这里!
让我们为一门叫做Pie的新语言擦拭并更新一些我们的旧玩具.
以下东西是一个Atom, 这显然吗?
'atom
2一点也不, Atom是什么意思?
要成为一个Atom, 就是要成为一个原子(atom).
注记. 在Lisp中, 原子可以是符号, 数字, 以及其他许多东西. 但在这里, 原子只是符号.
3那么, 'atom是一个Atom是因为'atom是一个原子.
以下东西是一个Atom, 这显然吗?
'ratatouille
译注. ratatouille指的是普罗旺斯杂烩.
4是的, 因为'ratatouille也是一个原子.

但是, 精确地说, 什么是一个原子呢?

原子是由一个单引号 (tick mark) 后跟着一个或更多的字母或者连字符而来的.
注记. 在Pie中, 只有原子使用单引号.
5因此, 那么
'is-it-obvious-that-this-is-an-atom
显然是一个Atom吗?
当然了, 因为原子可以包含连字符.
那么
'---
---
'
如何, 它们是原子吗?
6
'---
是一个原子, 因为连字符可以出现在一个原子里的任何位置;
---
不是一个原子, 因为它缺少了单引号;
'
不是一个原子, 因为它的后面既没有跟着一个字母, 也没有跟着一个连字符.
'Atom是一个Atom吗?7是的, 即便'Atom也是一个Atom, 因为它是一个单引号后面跟着一个或更多的字母或者连字符.
'at0m是一个Atom吗?8不是, 因为根据第1章第5框, 原子只能包含字母或者连字符, 而字符0并不是一个字母, 它是数位零.
'cœurs-d-artichauts是一个Atom吗?
译注. cœurs d'artichauts是一种食物, 即洋蓟心.
9是的, 因为œ是一个字母.
'ἄτομον是一个Atom吗?
译注. ἄτομον是一个古希腊语词, 意思是不可分割的东西, 即原子.
10That's Greek to me!
译注. 这算是一语双关.
但是希腊字母也是字母, 所以它必然是一个Atom.
单引号之律

一个单引号后直接跟着一个或更多的字母或者连字符则为一个Atom.

诸如

'ratatouille是一个Atom

'cœurs-d-artichauts是一个Atom

这样的句子被称为判断(judgment).
注记. 感谢Per Martin-Löf (1942–).
11判断的要义在于什么?
一个判断是一个人对于一个表达所采取的一个态度. 当我们得以获知什么东西时, 我们就在作出一个判断.
关于Atom'courgette, 可以判断什么?
12'courgette是一个Atom.
译注. courgette指的是西葫芦.
一个判断形式(form of judgment)是一个带有空白的观察, 例如

____是一个____.

13还有其他的判断形式吗?
判断 (judgment)的另一种形式是判断 (judgement).14十分有趣.
'ratatouille
'ratatouille
是相同的
Atom
吗?
15是的.

它们之所以是相同的Atom, 是因为单引号之后有着相同的字母.

'ratatouille
'courgette
是相同的
Atom
吗?
16不是.

单引号之后, 它们有着不同的字母.

单引号之诫

两个表达式为相同的Atom, 如果它们的值是单引号后跟着完全等同的字母和连字符.

第二种判断形式为

____和____是相同的____.

17因而

'citron'citron是相同的Atom

是一个判断.
译注. citron指的是香橼.
这的确是一个判断, 而且我们有理由去相信.

'pomme'orange是相同的Atom

是判断吗?

18诚然它是判断, 但是我们没有理由去相信它. 毕竟, 我们不该比较苹果 (apple) 和橙子.
译注. pomme是法语的苹果.
(cons 'ratatouille 'baguette)
是一个
(Pair Atom Atom)
这显然吗?
注记. 当准备好的时候, 读[page 62]看定型 (typing)指令.
19不, 完全不是.

成为一个

(Pair Atom Atom)
是什么意思呢?

译注. baguette指的是法棍面包.
成为一个
(Pair Atom Atom)
就是要成为一个序对, 其car是一个Atom, 例如'ratatouille, 其cdr也是一个Atom, 例如'baguette.
20cons, car, cdr看上去很眼熟. 不谈以前, 这里它们是什么意思呢? 它们和序对 (pair) 又有什么关系呢?
一个序对以cons起手, 而以另外两个部分作结, 我们称其为它的carcdr.
注记. 在Lisp中, cons可以使得列表更长. 但在这里, cons只是用来构造序对.
21好吧, 这意味着之所以
(cons 'ratatouille 'baguette)
是一个
(Pair Atom Atom)
是因为(cons 'ratatouille 'baguette)是一个序对, 其car是一个Atom, 其cdr也是一个Atom.

那么, cons是一个Pair吗?

甚至consPair在单独情况下都不是表达式, 它们都需要两个参数.

(cons 'ratatouille 'baguette)
(cons 'ratatouille 'baguette)
是相同的
(Pair Atom Atom)
吗?

注记. 在Lisp中, cons是一个过程, 有着其自身的含义, 但是诸如condlambda这样的形式如果单独出现则是没有意义的.
22两个表达式为相同的
(Pair Atom Atom)
是什么意思呢?
这意味着它们的car都是相同的Atom, 它们的cdr也都是相同的Atom.23那么的确
(cons 'ratatouille 'baguette)
(cons 'ratatouille 'baguette)
是相同的
(Pair Atom Atom)
(cons 'ratatouille 'baguette)
(cons 'baguette 'ratatouille)
是相同的
(Pair Atom Atom)
吗?
24
(cons 'ratatouille 'baguette)
car'ratatouille
(cons 'baguette 'ratatouille)
car'baguette.

因此, 我们没有理由去相信它们是相同的(Pair Atom Atom).

(cdr
 (cons 'ratatouille 'baguette))
可以怎样描述呢?
25它是一个
Atom
描述其他表达式的表达式, 例如Atom, 被称为类型(type).

(Pair Atom Atom)是一个类型吗?

注记. 当一个名字 (例如Pair或者Atom) 牵涉类型时, 首字母会大写.
26是的, 因为它描述了carcdr均为Atom的序对.
第三种判断形式为

____是一个类型.

27这意味着

Atom是一个类型

(Pair Atom Atom)是一个类型

都是判断.
Atom之律

Atom是一个类型.

'courgette是一个类型.

是一个判断吗?
28它的确是一个判断, 但是我们没有任何理由去相信它, 因为'courgette并不描述其他表达式.
AtomAtom是相同的类型吗?29想必如此, 它们看上去就应该是相同的类型.
第四种判断形式, 也是最后一种, 如下

____和____是相同的类型.

30那么, 所以说

AtomAtom是相同的类型

是一个判断, 并且我们有理由去相信它.
判断的四种形式
  1. ____是一个____.
  2. ____和____是相同的____.
  3. ____是一个类型.
  4. ____和____是相同的类型.
以下是一个判断吗?

Atom(Pair Atom Atom)是相同的类型.

31它是一个判断, 但是没有理由去相信它.
(Pair Atom Atom)
(Pair Atom Atom)
是相同的类型吗?
32看起来相当可信啊.
判断是获知的行为, 而相信是获知的一部分.33判断难道不是句子吗?
句子从理解它们的人那里获得意义. 句子捕获了我们所拥有的思想, 而思想比我们用来表达思想的词语要重要得多.34啊, 所以说得以获知
(Pair Atom Atom)
(Pair Atom Atom)
是相同的类型这一行为是一个判断.
的确如此.

'pêche'pêche是相同的'fruit吗?

译注. pêche指的是桃子.
35好问题.

'pêche是一个'fruit吗?

不, 当然不是, 因为

'fruit是一个类型

并不可信.

某些形式的判断只有在早前的判断的基础之上才具备意义.

注记. 这种早前的判断有时被称为前置假定(presupposition).
36有哪些呢?
为了提问一个表达式是否由一个类型描述, 我们必须已经判断过给定的类型的确是一个类型. 为了提问两个表达式是否在一个类型下是相同的, 我们必须首先判断出这两个表达式都由该类型所描述.
注记. 当然, 为了描述那两个表达式, 那个给定的类型也得是类型.
在提问两个表达式是否是相同的类型之前, 有什么判断是必要的吗?
37为了提问两个表达式是否是相同的类型, 我们必须先要判断这两个表达式的确都是类型.
(car
 (cons 'ratatouille 'baguette))
'ratatouille
是相同的
Atom
吗?
38看起来十分熟悉啊. 想必是这样, 因为car可以找到一个序对的car, 所以它们相同的.
(cdr
 (cons 'ratatouille 'baguette))
'baguette
是相同的
Atom
吗?
39诚然如此, 因为这个序对的cdr'baguette.
于是
(car
 (cons
  (cons 'aubergine 'courgette)
  'tomato))
是一个...
译注. aubergine指的是茄子.
40... (Pair Atom Atom), 因为
(cons 'aubergine 'courgette)
是一个序对, 其carAtom 'aubergine, 其cdrAtom 'courgette.
(car
 (cdr
  (cons
   'ratatouille
   (cons 'baguette 'olive-oil))))
'baguette
是相同的
Atom
吗?
41是的, 的确如此.
正如从第1章第39框第1章第41框所展现的那样, 写法不同的表达式或许可能是相同的. 其中一种写法要比其他写法都更加直接.42'baguette的确看上去比
(car
 (cdr
  (cons
   'ratatouille
   (cons 'baguette 'olive-oil))))
更加直接.
一个表达式的规范形式(normal form)是书写该表达式最直接的方式. 任何两个相同的表达式都有着等同的规范形式, 并且任何两个有着等同规范形式的表达式都是相同的.43'olive-oil
(cdr
 (cdr
  (cons
   'ratatouille
   (cons 'baguette 'olive-oil))))
的规范形式吗?
这个问题是不完整的.

相同总是相对于某个类型而言的, 因此规范形式也由类型决定.

44'olive-oilAtom
(cdr
 (cdr
  (cons
   'ratatouille
   (cons 'baguette 'olive-oil))))
的规范形式吗?
是的, 的确如此.
(cons 'ratatouille 'baguette)
是一个规范的
(Pair Atom Atom)
吗?
注记. 规范的(normal)具有规范形式(in normal form)的简略说法.
45是的, (cons 'ratatouille 'baguette)的确是规范的.

每个表达式都具有一个规范形式吗?

如果不刻画一个表达式的类型, 那么提问其是否具有规范形式也是没有意义的.

然而, 给定一个类型, 每个由该类型描述的表达式的确都有一个由该类型确定的规范形式.

46如果了两个表达式根据它们的类型是相同的, 那么它们就具有等同的规范形式. 因此, 这必然意味着我们可以通过比较两个表达式的规范形式来判断 (check) 它们是否相同.
规范形式

给定一个类型, 每个由该类型所描述的表达式都具有一个规范形式, 这是书写该表达式最直接的方式. 如果两个表达式是相同的, 那么它们有着等同的规范形式. 如果两个表达式有着等同的规范形式, 那么它们是相同的.

(car
 (cons
  (cons 'aubergine 'courgette)
  'tomato))
的规范形式是什么?
47类型是什么呢?

如果类型是

(Pair Atom Atom)
那么规范形式为
(cons 'aubergine 'courgette)

干得好!

之前我们对于什么是一个

(Pair Atom Atom)
的描述其实是不完整的, 而完整的描述应该是...

48...成为一个序对, 其car是一个Atom, 其cdr也是一个Atom, 或者是与这样一个序对相同的一个表达式.
规范形式和类型

相同总是根据一个类型来的, 因而规范形式也由类型决定.

(car
 (cons
  (cons 'aubergine 'courgette)
  'tomato))
(cons 'aubergine 'courgette)
是相同的
(Pair Atom Atom)
吗?
49是的, 之所以这两个表达式是相同的(Pair Atom Atom), 是因为
(car
 (cons
  (cons 'aubergine 'courgette)
  'tomato))
的规范形式是
(cons 'aubergine 'courgette)
为什么
(cons 'aubergine 'courgette)
(cons 'aubergine 'courgette)
是相同的(Pair Atom Atom)呢?
50这看起来非常显然?
是的, 但是不是每个看上去显然的东西实际上都是显然的.

第1章第23框描述了何谓一个表达式和另一个表达式是相同的

(Pair Atom Atom)

注记. 在Lisp中, 相同的原子使用两次cons产生的序对并不eq. 但在这里, 它们无法以任何方式进行区分.
51
(cons 'aubergine 'courgette)
(cons 'aubergine 'courgette)
的顶层都是cons, 'aubergine'aubergine是相同的Atom, 'courgette'courgette是相同的Atom.

这两个表达式具有相同的car和相同的cdr, 因而它们是相同的

(Pair Atom Atom)

cons之第一诫

两个cons表达式是相同的(Pair A D), 如果它们的car是相同的A而它们的cdr是相同的D, 其中AD代表任意的类型.

很好.
(Pair
 (cdr
  (cons Atom 'olive))
 (car
  (cons 'oil Atom)))
的规范形式是什么呢?
52我猜是(Pair 'olive 'oil), 是这样吗?
实际上, 表达式
(Pair
 (cdr
  (cons Atom 'olive))
 (car
  (cons 'oil Atom)))
既不由某个类型刻画, 本身也不是一个类型, 因此提问其规范形式是毫无意义的.
注记. 不被类型刻画且自身不是类型的表达式也被称为是病态类型(ill-typed)的.
53为什么呢?
因为Pair在其参数为实际原子时并非类型.

只有在其参数均为类型 (例如Atom) 时, 它才是一个表达式.

54这是不是意味着Pair不能和carcdr一起使用呢?
不, 完全不是.
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
的规范形式是什么?
55它的类型是什么呢? 规范形式总是相对于某个类型而言的.
类型本身也有规范形式. 如果两个类型有着等同的规范形式, 那么它们就是相同的类型. 如果两个类型是相同的类型, 那么它们就具有等同的规范形式.56类型
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
的规范形式必然是(Pair Atom Atom), 因为
(car
 (cons Atom 'olive))
的规范形式为Atom, 并且
(cdr
 (cons 'oil Atom))
的规范形式也是Atom.
类型的规范形式

每个为类型的表达式都具有一个规范形式, 其是书写该类型最直接的方式. 如果两个表达式是相同的类型, 那么它们有着等同的规范形式. 如果两个类型有着等同的规范形式, 那么它们是相同的类型.

就是这样. 现在我们知道
(cons 'ratatouille 'baguette)
也是一个
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
因为...
57...
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
的规范形式为
(Pair Atom Atom)
(cons 'ratatouille 'baguette)
是一个
(Pair Atom Atom)
另一种说法是
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
(Pair Atom Atom)
是相同的类型.
58如果一个表达式是一个
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
那么它也是一个
(Pair Atom Atom)
因为这两个类型是相同的类型.
类似地, 如果一个表达式是一个
(Pair Atom Atom)
那么它也是一个
(Pair
 (car
  (cons Atom 'olive))
 (cdr
  (cons 'oil Atom)))
因为这两个类型是相同的类型.
59对于
(Pair
 Atom
 (cdr
  (cons 'oil Atom)))
也是类似的, 因为它和前面两个也是相同的类型.
'6是一个Atom吗?60不是, 我们没有理由去相信

'6是一个Atom

因为数位6既非字母也非连字符, 不是吗?
的确如此.
(cons '17 'pepper)
是一个
(Pair Atom Atom)
吗?
61不, 因为(cons '17 'pepper)car'17, 它并非一个Atom.

尽管如此, 拥有数字应该也是很自然的事情才对.

数字当然是很方便的. 除了AtomPair, 我们还可以判断某个东西是否是一个Nat.62让我们来试一下.
1是一个Nat吗?
注记. Nat自然数(natural number)的缩写.
63的确, 1是一个Nat.
1729是一个Nat吗?64是的, 1729是一个Nat. 它不仅是一个Nat, 还很著名!
注记. 谢谢Srinivasa Ramanujan (1887–1920) 和Godfrey Harold Hardy (1877–1947).
-1是一个Nat吗?65嗯, 你确定?
不, 当然不是. -23呢?66不是很清楚啊.
正数是Nat.67啊, 那么-23不是一个Nat?
我们更喜欢采取积极 (positive) 的态度.
译注. 这大概是一语双关吧.
最小的Nat是什么?
680不是一个自然数吗?
啊, 所以我们也不能总是很积极. 我们该如何获得剩下来的Nat呢?
注记. 然而, 数字1正的.
69我们可以使用我们的老朋友add1. 如果n是一个Nat, 那么(add1 n)也是一个Nat, 并且这个新的Nat永远是正数, 即便n0.

有多少个Nat呢?

很多很多!70存在最大的Nat吗?
并不, 因为我们总是可以...71...使用add1来加上一?
的确是这样!

026是相同的Nat吗?

注记. 谢谢Giuseppe Peano (1838-1932).
72显然不是.
(+ 0 26)26是相同的吗?
注记. 即便我们还没有解释+, 暂时请用你自己对于加法的理解.
73这个问题没有意义. 但是, 我们可以问它们是否是相同的Nat吗?
当然可以.

(+ 0 26)26是相同的Nat吗?

74是的, 这是因为(+ 0 26)的规范形式为26, 而2626当然是相同的.
zero的意思是什么呢?75zero0是相同的吗?
在Pie里, zero0不过是书写同一个Nat的不同方式而已.

one1是相同的Nat吗?

76嗯, 如果zero0是相同的Nat的话, 那么这似乎很合理.
实际上, one没有意义. 但是, (add1 zero)是书写数字1的另一种方式.

通过定义使得one(add1 zero)的确是可行的.

(define one
  (add1 zero))

77为什么这个定义周围用虚线框住了呢?
虚线框意味着这个定义有点问题, 以至于它不能在之后使用.78这个定义有什么问题呢?

看上去很正常啊.

当定义一个名字时, 有必要先claim这个名字具有一个类型, 而one是一个Nat.
(claim one Nat)
(define one
  (add1 zero))
79因此, two可以被定义成
(claim two Nat)
(define two
  (add1 one))
定义前先声明

使用define将一个名字和一个表达式联系起来之前需要使用claim将名字和表达式的类型联系起来.

如果1是书写(add1 zero)的另一种方式, 那么书写4的另一种方式是什么呢?80难道不应该是
(add1
 (add1
  (add1
   (add1 zero))))
吗? 我们不能定义four来指代这个表达式吗?
当然可以了.
(claim four Nat)
(define four
  (add1
   (add1
    (add1
     (add1 zero)))))
那么, 再问一下书写8有另外的方式吗?
81那必然是
(add1
 (add1
  (add1
   (add1
    (add1
     (add1
      (add1
       (add1 zero))))))))
8是规范的吗?82似乎如此, 但是为什么8是规范的呢?
之所以8规范的, 是因为其顶add1是一个构造子(constructor), 并且塞在顶add1下面的参数, 即7, 也是规范的.
注记. 第1章第81框中的顶add1只此一次用下划线标注出来以示强调.
83为什么7, 亦写作
(add1
 (add1
  (add1
   (add1
    (add1
     (add1
      (add1 zero)))))))
是规范的呢?
7是规范的完全是同理可得.84这意味着zero必然是规范的, 不然的话(add1 zero)就不是规范的了.
zero的顶是什么呢?85必须是zero.
之所以zero是规范的, 是因为顶zero是一个构造子, 并且其没有参数.
(add1
 (+ (add1 zero)
    (add1
     (add1 zero))))
是规范的吗?
86不是, 因为+不是构造子.
一个以构造子为顶的表达式被称为一个值(value).

即便

(add1
 (+ (add1 zero)
    (add1
     (add1 zero))))
不是规范的, 它的确是一个值.

注记. 值也被称为典则(canonical)表达式.
87这个表达式不是规范的, 是因为
(+ (add1 zero)
   (add1
    (add1 zero)))
并非书写3的最直接方式.

以构造子为顶的表达式被称为.

现在我们给出另一个并非规范的表达式.
(+ (add1
    (add1 zero))
   (add1 zero))
这是书写3的最直接方式吗?
88肯定不是.

准确来说, 构造子究竟是什么呢?

某些表达式是类型, 例如Nat(Pair Nat Atom).

对于新类型的解释的一部分是要说明其构造子为何. 构造子表达式是构造具有该类型的表达式的直接方式.

89构造子的例子有哪些呢?
Nat的构造子是zeroadd1, 而Pair的构造子是cons.90值和规范形式之间有何关系?
在一个值里, 顶层的构造子的参数不必是规范的. 但如果这些参数的确是规范的, 那么整个构造子表达式就具有规范形式.

所有的值都是规范的吗?

91显然不是.
(add1
 (+ (add1 zero)
    (add1
     (add1 zero))))
(add1
 (+ (add1 zero) (add1 one)))
都是值, 但是它们都不是规范的.
值和规范形式

不是所有的值都具有规范形式. 这是因为构造子的参数不必是规范的. 每个表达式只有一个规范形式, 但是有时可能将其写成值的方式不止一种.

以下空白方框里填什么会使得整个表达式是一个Nat值呢?
(add1 )
92'aubergine怎么样?
诚然如此.
(add1 'aubergine)
并非一个Nat值, 因为'aubergine是一个Atom而不是一个Nat.

当填充这样的方框时, 我们的期望是作为结果的表达式由某个类型刻画.

93然而, 若置于该方框里的是任何一个Nat表达式, 那么整个表达式就一个值. 这一整个表达式以add1为顶, 而add1是一个Nat构造子.
找出一个与某起始表达式相同的值被称为求值(evaluation).94类型呢? 毕竟, 相同需要类型.
有时当我们提及相同时, 我们并不显式提及类型. 然而, 总是存在一个意图的类型, 并且这个类型可以通过仔细阅读找到.95难道求值指的不是找到一个表达式的意义(meaning)吗? 这不只是某个更简单的表达式.
我们这里的含义不一样. 表达式并不指向某种外部的意义概念——在Pie中, 除了表达式和我们对于表达式的判断之外, 别无其他.
注记. 在Lisp中, 值和表达式是不同的, 而求值的结果是一个值.
96这是一种看待求值的新方式.

为什么规范形式和值之间要做区分呢?

每个东西都是一个表达式

在Pie中, 值也是表达式. Pie中的求值是寻找一个表达式, 而不是其他别的什么东西.

规范表达式没有可供求值的剩余机会了. 通常而言, 规范的表达式更容易理解. 然而, 往往找到一个值就足够了, 因为顶层的构造子可以用来判断接下来必然发生的事情.97如果找到一个值经常就足够了的话, 难道说这意味着我们可以自由地去寻找值, 并且然后可以想停就停呢?
是这样的, 只要关于构造子的参数的信息从没有用到即可.
(add1
 (+ (add1 zero)
    (add1
     (add1 zero))))
four是相同的Nat吗?
98这里给出一个可能的回答.

它们不是相同的Nat, 因为

(add1
 (+ (add1 zero)
    (add1
     (add1 zero))))
是一个值, 而且它当然长得不像变量four. 找到four的值也无济于事, 因为four的值看起来非常不同.

算是好的尝试.

但是, 实际上它们是相同的Nat.

99怎么能这样呢?
两个不是值的Nat表达式相同, 如果它们的值相同. 恰存在两种Nat值可以相同的方式: 每种构造子一个.

如果两个都是zero, 那么它们就是相同的Nat.

100那么两个值都以add1为顶的情况呢?
zero之诫

zerozero是相同的Nat.

如果每个add1的参数是相同的Nat, 那么两个add1表达式是相同的Nat值.

为什么

(add1 zero)
(add1 zero)
是相同的
Nat

101这两个表达式都是值. 这两个值都以add1为顶, 因此它们的参数应该是相同的Nat.

这两个参数都是zero, zero是一个值, 而且zerozero是相同的Nat值.

add1之诫

如果nk是相同的Nat, 那么(add1 n)(add1 k)是相同的Nat.

为什么
(add1 (+ 0 1))
(add1 (+ 1 0))
是相同的
Nat
102这两个Nat都以add1为顶, 于是它们都是值.

它们之所以相同, 是因为

(+ 0 1)
(+ 1 0)
是相同的
Nat

为什么(+ 0 1)(+ 1 0)是相同的Nat?103这些Nat并非值, 因而为了判断它们是否相同, 第一步应该是找出它们的值.

这两个表达式都以(add1 zero)为其一个值, 而第1章第101框解释了为什么

(add1 zero)
(add1 zero)
是相同的
Nat

很对.104是否这意味着four本可以按照以下方式定义?
(define four
  (add1
   (+ (add1 zero)
      (add1
       (add1 zero)))))
为什么有虚线框呢?105four已经被定义了, 因而不能被再次定义.
定义是永恒的

一旦一个名字被claim了, 那么它就不能被重新claim. 一旦一个名字被define了, 那么它就不能被重新define.

不过当然了, 一开始four的确可以像那样定义.

实际上, 其他表达式都不能分辨出four的两种定义之间的不同, 因为这两个定义了four为相同的Nat.

106cons是一个构造子吗?
是的, cons构造Pair.107为了对于car表达式求值, 是否有必要对于car的参数求值?
的确. 为了找出一个car表达式的值, 我们从找出其参数的值开始.

关于这参数的值, 我们能说什么呢?

108这参数的值以cons为顶.
在找出参数的值之后, 接下来应该做什么呢?109整个表达式的值是cons的第一个参数.
(car
 (cons (+ 3 5) 'baguette))
的值是什么?
110cons的第一个参数是
(+ 3 5)
这并非一个值.
为了找出一个car表达式的值, 首先找出其参数的值, 这应该是(cons a d), 而
(car
 (cons a d))
的值然后就是a.

如何找出一个cdr表达式的值呢?

注记. 这里的a代表card代表cdr.
111就和car一样, 我们从对于cdr的参数求值开始, 直至其变为(cons a d), 然后
(cdr
 (cons a d))
的值就是d的值.

所有的构造子都有参数吗?

当然不是, 回忆一下, 第1章第86框里的zero是一个构造子.

两个表达式为相同的(Pair Atom Nat)是什么意思?

112这必然意味着每个表达式的值都以cons为顶, 并且它们的car是相同的Atomcdr是相同的Nat.
非常好.113原子是构造子吗?
原子'bay是一个构造子, 因而原子'leaf也是一个构造子.114所有原子都是构造子吗?
是的, 每个原子都构造其自身.

这是不是意味着每个原子都是值?

115的确, 因为解释为什么

Atom是一个类型

就是在说原子是Atom值.
嗯.

在表达式zero中, 顶层构造子是什么?

116那必然是zero, 因为zero是没有参数的构造子.
对于表达式'garlic而言, 什么是顶层的构造子?117原子'garlic是仅有的构造子, 所以它必然就是顶层的构造子.

那么, Nat是一个构造子吗?

不是, Nat并非一个构造子. zeroadd1是创造数据的构造子, 而Nat描述了特定的数据, 其要么就是zero, 要么以add1为顶, 且以另一个Nat为其参数.

Pair是一个构造子吗?

118不是, 因为Pair表达式是在描述以cons为顶的表达式. 构造子创建数据, 而不是类型.

那么, Pair应该叫做什么呢?

Pair是一个类型构造子(type constructor), 因其构造了一个类型. 类似地, NatAtom也是类型构造子.
(cons zero 'onion)
是一个
(Pair Atom Atom)
吗?
119不是.

难道它不应该是一个

(Pair Nat Atom)
吗?

的确如此! 但是
(cons 'zero 'onion)
是一个
(Pair Atom Atom)
请问
(cons 'basil
 (cons 'thyme 'oregano))
的类型是什么?
注记. 谢谢Julia Child (1912-2004).
120基于我们的所见所闻, 它必然是一个
(Pair Atom
 (Pair Atom Atom))
译注. basil, thyme, oregano分别是罗勒, 百里香, 牛至, 这是三种典型的香料. 另外, Julia Child是一位厨师和电视名人.
诚然如此.121好吧, 暂时就那么多了, 我的脑袋要炸了!
或许你应该再一次阅读这一章. 判断, 表达式, 类型是本书最重要的概念.122在完全读完这一章之后, 或许应该来点新鲜蔬菜.

第2章 从心所欲, 道法自然

ratatouille如何?1很好(très bien), 谢谢提问.
1章里有构造子和类型构造子, 分别构造值和类型.

然而, car既不是构造子也不是类型构造子.

2那么, car是什么呢?
car是一个消去子(eliminator). 消去子将构造子构造的值拆开来.

另一个消去子是什么呢?

3如果car是一个消去子, 那么显然cdr也是一个消去子.
构造子和消去子

构造子构造值, 而消去子将构造子构造的值拆开来.

另一种看待(消去子和构造子的)不同的方式在于值包含信息, 而消去子允许我们去利用这样的信息.4存在兼作构造子和消去子的东西吗?
不, 并不存在.

可以定义一个函数(function), 其表达力相当于carcdr的联合.

5怎么做呢?
这需要请出我们的老朋友λ.6这是什么? 我有点陌生.
Oops! 它也被称为lambda.
注记. λ可以选择写成lambda.
7好吧, λ可以构造函数.

这是不是意味着λ是一个构造子呢?

是的, 的确如此, 因为每个长得像(λ (x0 x ) body)的表达式都是一个值.

这种值的消去子是什么呢?

注记. 记号x 的意思是零个或更多的x, 因此x0 x 的意思是一个或更多的x.
8我们唯一能对函数做的事情就是将其应用于参数上.

这样的话函数怎么拥有消去子呢?

应用函数于参数函数的消去子.9好吧.
消去函数

应用函数于参数函数的消去子.

(λ (flavor)
  (cons flavor 'lentils))
的值是什么?
译注. lentils指的是小扁豆.
10它自λ起, 因而它已经是一个值了.
是的.
((λ (flavor)
   (cons flavor 'lentils))
 'garlic)
的值是什么呢?
11那必然是(cons 'garlic 'lentils), 如果λlambda的行为一致且cons是一个构造子的话.

但是这难道不意味着即便cons表达式已经是一个值了, cons的第一个参数仍然会被求值吗?

不, 并非如此, 但是这是个非常好的问题. 替换这个λ表达式的flavor行为之发生是因为该λ表达式被应用于了一个参数, 而不是因为cons.
注记. 一致地替换一个变量以一个表达式有时被称为替换(substitution).
这个λ表达式的体的每个flavor都会被替换为'garlic, 不论环绕flavor的表达式是什么.
12因此, 也就是说
((λ (root)
   (cons root
         (cons (+ 1 2) root)))
 'potato)
的值应该是
(cons 'potato
      (cons (+ 1 2) 'potato))
对吗?
为什么前一个框里的
(+ 1 2)
不需要计算呢?
13整个表达式以cons为顶, 故其已是值.
第2章第12框里有点夸大其词. 如果以下λ表达式的体中的root出现在另一个同名的λ之下的话, 那么它就不会被替换了.
((λ (root)
   (cons root
         (λ (root) root)))
 'carrot)
的值是什么?
译注. 这里的例子有两个同名但不同的变量, 以示对比.
14那必然是
(cons 'carrot
      (λ (root) root))
因为内层的root出现在一个同名的λ表达式之下.
λ的确和lambda行为一致, 因而这的确是正确的答案.

成为一个

(→ Atom (Pair Atom Atom))
就是要成为一个λ表达式, 当其应用于一个Atom作为参数时, 则求值至一个
(Pair Atom Atom)

注记. (→ Atom (Pair Atom Atom))读作箭头 原子 暂停 序对 原子 原子, 并且可以写成是两个字符的版本: ->.
15那以这样的λ表达式为值的表达式呢?
是的, 那些表达式也是
(→ Atom (Pair Atom Atom))
因为当给出一个Atom作为其参数时, 它们也会变成
(Pair Atom Atom)
16它们也是
(→ (car (cons Atom 'pepper))
   (Pair (cdr (cons 'salt Atom))
         Atom))
吗?
的确如此, 因为
(car (cons Atom 'pepper))
Atom
(cdr (cons 'salt Atom))
也是Atom.
17提问何谓两个表达式为相同的Nat, Atom, (Pair Nat Atom)都是有意义的.

提问何谓两个表达式为相同的

(→ Nat Atom)
或者相同的
(→ (Pair Atom Nat) Nat)
也是有意义的吗?

是的, 的确如此. 两个表达式为相同的
(→ Nat Atom)
当它们的值是相同的
(→ Nat Atom)
18它们的值是λ表达式. 两个λ为相同的
(→ Nat Atom)
是什么意思呢?
两个接受 (expect) 相同数目的参数的λ是相同的, 如果它们的体是相同的. 例如, 两个λ表达式是相同的
(→ Nat (Pair Nat Nat))
如果它们的体是相同的
(Pair Nat Nat)
19这意味着
(λ (x) (cons x x))
(λ (y) (cons y y))
不是相同的
(→ Nat (Pair Nat Nat))
吗?
这两个表达式有什么不同之处呢?20参数的名字是不同的. 尽管如此, 这通常无关紧要. 现在紧不紧要呢?
两个λ表达式也是相同的, 如果可以通过一致地对于参数换名使得它们的体变得相同.
注记. 一致地对于变量换名常被称为α变换(alpha-conversion). 感谢Alonzo Church (1903-1995).
一致地对于变量换名不会改变任何东西的意义.
21
(λ (a d) (cons a d))
(λ (d a) (cons a d))
是相同的
(→ Atom Atom (Pair Atom Atom))
吗?
应用的初规则

如果f是一个

(→ Y X)
arg是一个Y, 那么
(f arg)
是一个X.

λ初第一诫

两个接受 (expect) 相同数目的参数的λ是相同的, 如果在一致地对于它们的变量进行换名之后, 它们的体是相同的.

λ初第二诫

如果f是一个

(→ Y X)
那么f
(λ (y) (f y))
是相同的
(→ Y X)
只要y不出现在f中.

不, 并非如此, 因为对于第二个λ表达式中的变量进行一致换名以匹配第一个λ表达式的参数将会产生
(λ (a d) (cons d a))
(cons d a)(cons a d)并非相同的(Pair Atom Atom).
22
(λ (y) (car (cons y y)))
呢? 它和
(λ (x) x)
是相同的
(→ Nat Nat)
吗?
对于变量换名之律

一致地对于变量换名不会改变任何东西的意义.

首先, 一致地将y换名为x. 现在, 问题就变为
(car (cons x x))
x是否是相同的Nat.
23恰有两种方式可以使得两个表达式成为相同的Nat. 一种是它们的值都是zero. 另一种是它们的值都以add1为顶, 而这两个add1的参数是相同的Nat.

这些表达式并非Nat值, 因为它们既不以add1为顶, 也不是zero.

x的值尚不可知, 因为这个λ表达式还没有被应用于一个参数. 但是, 当这个λ表达式已被应用于一个参数时, x的值仍然是一个Nat, 因为...24...因为这个λ表达式是一个
(→ Nat Nat)
故参数x不可能是任何其他什么东西.
并非值且因为变量的缘故不能被求值的表达式被称为中立(neutral)的.25这意味着
(cons y 'rutabaga)
是中立的咯?
译注. rutabaga是芜菁甘蓝的意思.
不, 它并非中立, 因为
(cons y 'rutabaga)
是一个值.

如果x是一个(Pair Nat Atom), 那么

(cdr x)
是一个值吗?

26不是, 因为cdr是一个消去子, 而消去子把值拆散.

在不知道x的值的情况下, 没有办法找出(cdr x)的值, 故(cdr x)是中立的.

中立表达式使得我们有必要扩展对于何谓相同的看法. 每个变量与其自身都是相同的, 不管其类型如何. 这是因为变量只能被一致地替换, 所以说一个变量的两次出现不可能被代之以不同的值.27因此, 如果我们假定y是一个Nat, 那么
(car (cons y 'rutabaga))
y是相同的Nat, 这是因为该car表达式的规范形式是y, 而yy是相同的Nat.
的确如此, 并且类似地, 我们有
(λ (x) (car (cons x x)))
(λ (x) x)
是相同的
(→ Nat Nat)
28是的, 因为中立表达式xx是相同的Nat.
(λ (x) (car x))
(λ (y) (car y))
是相同的
(→ (Pair Nat Nat) Nat)
吗?
29我们应该会这样认为, 但是理由是什么呢?
第一步应该是一致地将y换名为x.

(λ (x) (car x))
(λ (x) (car x))
是相同的
(→ (Pair Nat Nat) Nat)
吗?

30是的, 如果假定
(car x)
(car x)
是相同的Nat.

但是(car x)并不是一个变量, 在知道x的值之前我们都不可能找出(car x)的值.

如果两个表达式以等同的消去子为顶, 并且两个消去子的参数是相同的, 那么这两个表达式也是相同的. 字面上等同的中立表达式是相同的, 不管它们的类型如何.31因而
(car x)
(car x)
是相同的Nat, 若假定x是一个(Pair Nat Nat).
中立表达式之诫

字面上等同的中立表达式是相同的, 不管它们的类型如何.

(λ (a d) (cons a d))
是一个
(→ Atom Atom (Pair Atom Atom))
吗?
32后面跟着更多的表达式意味着什么呢?
后面跟着的表达式, 除了最后一个, 都是参数的类型. 最后一个则是值的类型.
注记. 最后一个类型读的时候前面会有停顿.
33好吧, 那么
(λ (a d) (cons a d))
的确是一个
(→ Atom Atom (Pair Atom Atom))
这些表达式不可避免地正在逐渐变长.
一种缩短它们的方式是小心地使用define, 如第1章第77框那样, 其总是允许我们用简短的名字来代替冗长的表达式.34好想法.
设构造子cons被应用于'celery'carrot, 我们可以称这个值为vegetables.
(claim vegetables
  (Pair Atom Atom))
(define vegetables
  (cons 'celery 'carrot))
从现在开始, 每当名字vegetables被使用, 它就和
(cons 'celery 'carrot)
是相同的
(Pair Atom Atom)
因为这就是vegetables如何被define的.
译注. celery指的是西芹.
35为什么claim后面有写着
(Pair Atom Atom)
呢?
define之律和诫

遵照

(claim name X)(define name expr)

如果

expr是一个X

那么

name是一个X

并且

nameexpr是相同的X

(Pair Atom Atom)描述了我们可以怎样使用vegetables. 例如, 我们知道(car vegetables)是一个Atom, 而(cons 'onion vegetables)是一个
(Pair Atom (Pair Atom Atom))
注记. 这对于小扁豆汤 (lentil soup) 来说也是一个好的开始.
36啊, 懂了.
vegetables
(cons (car vegetables)
      (cdr vegetables))
是相同的
(Pair Atom Atom)
吗?
37的确如此, 因为每个表达式的值都是一个序对, 并且其car'celerycdr'carrot.
实际上, 每当p是一个(Pair Atom Atom)时, 那么p
(cons (car p) (cdr p))
是相同的
(Pair Atom Atom)
找出(car p)(cdr p)的值是没有必要的.
38这看起来很合理.
cons之第二诫

如果p是一个(Pair A D), 那么它和(cons (car p) (cdr p))是相同的(Pair A D).

以下定义可以允许吗?
(claim five Nat)
(define five (+ 7 2))
39什么鬼?
尽管可能是个愚蠢的想法, 但它是可以被允许的.
(+ five 5)
的规范形式是什么?
40那必然是10, 因为五 (five) 加5等于十.
再想想. 请记得five的奇怪定义...41...哦, 好吧, 那应该是14, 因为five被定义成了9.
的确如此.42这个定义可以允许吗? 似乎它看上去不那么蠢.
(claim zero Nat)
(define zero 0)
虽然和将five定义为9相比没那么愚蠢, 但是这个定义也是不被允许的.

已经被使用了的名字, 不论是用于构造子, 消去子, 还是之前的定义, 都不适合再与claim或者define一起使用了.

43好.
定义中的名字

在Pie语言中, 只有没有用过的名字, 才能和claim或者define一起使用, 不论是用作构造子, 消去子, 还是之前的定义.

Nat有一个消去子可以区分值为zeroNat和值以add1为顶的Nat. 这个消去子被称为which-Nat.44which-Nat到底是如何辨别应该是哪一种Nat的呢?
一个which-Nat表达式具有三个参数: target, base, step:
(which-Nat target
  base
  step)
which-Nat判断target是否是zero, 如果是, 那么整个which-Nat表达式的值即base的值. 否则的话, 如果target(add1 n), 那么整个which-Nat表达式的值即(step n)的值.
45因此, which-Nat既要判断一个数字是否是zero, 又要在数字并非zero时去除其顶的add1.
的确如此.
(which-Nat zero
  'naught
  (λ (n)
    'more))
的规范形式是什么呢?
46必然是'naught, 因为其target zerozero, 故整个which-Nat表达式的值是base, 即'naught.

为什么n是黯淡的?

黯淡是用来指出n在那个λ表达式的体里没有被使用. 没有被使用的名字都将以黯淡的形式出现.47为什么没有被使用呢?
which-Nat提供了使用更小的Nat的可能性, 但是它并不要求一定使用. 当然, 为了提供这种可能性, which-Nat的最后一个参数必须要接受一个Nat.48好.
黯淡的名字

没有被使用的名字是黯淡的, 但是它们的确有必要呆在那里.

(which-Nat 4
  'naught
  (λ (n)
    'more))
的值是什么?
49必然是'more, 因为4是另一种书写(add1 3)的方式, 而其以add1为顶.
((λ (n) 'more) 3)
的规范形式为
'more
which-Nat之律

如果target是一个Nat, base是一个X, 并且step是一个

(→ Nat X)
那么
(which-Nat target
  base
  step)
是一个X.

which-Nat之第一诫

如果

(which-Nat zero
  base
  step)
是一个X, 那么其和base是相同的X.

which-Nat之第二诫

如果

(which-Nat (add1 n)
  base
  step)
是一个X, 那么其和(step n)是相同的X.

50
51
52

课间: 一叉子Pie

1
2

第3章 消去所有的自然数!

第4章 小菜一碟, 简单如Pie

在[frame 2:70]里, 我们定义Pear
(claim Pear U)
(define Pear
  (Pair Nat Nat))
Pear的消去子是由carcdr定义的.
1并且...
Pear的一个消去子必须要做什么呢?2这个消去子必须要暴露 (或者说解包) 一个Pear中的信息.
Pair的消去子呢? 它必须要做什么?3Pair的一个消去子必然要暴露一个Pair中的信息.
那很接近了.
正如[frame 1:22]所言, Pair单独不是一个表达式, 然而
(Pair Nat Nat)
是一个表达式, 并且它有消去子.
(Pair Nat Atom)
也有消去子.
4再次尝试:
(Pair Nat Nat)
的一个消去子必然要暴露一个特定
(Pair Nat Nat)
中的信息, 一个
(Pair Nat Atom)
的消去子必然要暴露一个特定
(Pair Nat Atom)
中的信息.

第5章 表, 表, 更多的表