Skip to content

the plan

· xieyuheng

unnecessary entities ​

什么是不必要的元素?

首先是关于类型系统, 必要的是一种描述数据结构的方式, 能够描述就能表达和记录我们对问题的理解, 我经常称之为「建模」。 不必要的是把这种对数据结构的描述当成类型, 在编译时做静态类型检查。

define-data ​

比如,我们可以用谓词来灵活地描述数据结构, 用 define-data 语法来声明新的 data predicate 和相关的 data constructors:

scheme
(define-data exp?
  (var-exp (name string?))
  (apply-exp (target exp?) (arg exp?))
  (lambda-exp (parameter string?) (body exp?)))

经过上面的定义,我们会有:

scheme
exp?        ;; data predicate
var-exp     ;; data constructor
apply-exp   ;; data constructor
lambda-exp  ;; data constructor
var-exp?    ;; data constructor predicate
apply-exp?  ;; data constructor predicate
lambda-exp? ;; data constructor predicate

还可以利用 data constructor 中所声明的 data field name, 来自动定义 data accessors:

scheme
var-exp-name
apply-exp-target
apply-exp-arg
lambda-exp-parameter
lambda-exp-body

也可以类似地生成 data modifiers:

scheme
var-exp-put-name!
apply-exp-put-target!
apply-exp-put-arg!
lambda-exp-put-parameter!
lambda-exp-put-body!

注意,为了 currying, 被 put 的 value 是第一个参数, 而 data 是第二个参数。

还可以生成一批不带副作用的 data modifiers:

scheme
var-exp-put-name
apply-exp-put-target
apply-exp-put-arg
lambda-exp-put-parameter
lambda-exp-put-body

比如 my-list:

scheme
(define-data (my-list? E)
  (nil)
  (li (head E) (tail (my-list? E))))

会生成的 name 有:

scheme
my-list?
nil
li
nil?
li?
li-head
li-tail

注意,类似 generic type 的定义, generic predicate 需要给出 generic 参数(也是 predicate):

scheme
(assert (my-list? int? (li 1 (li 2 (li 3 (nil))))))

但是 data constructor 所生成的 predicate 不需要带有 generic 参数:

scheme
(assert (li? (li 1 (li 2 (li 3 (nil))))))

这种谓词是很容易实现的, 因为不同的 data constructor 所构造出来的 data 都不一样, 可以通过其 constructor 来区分。 constructor 之间通过 module + name 就可以区分。

predicate combinators ​

我们还是可以保留 x-sexp 所定义的基础数据。 并且可以把 lattice-lisp 中的 type constructors 和 type operators 当作 predicate combinator:

scheme
(assert ((tau int? int? :x int? :y int?) [1 2 :x 1 :y 2]))

由于是动态类型系统,不用考虑类型之间的子类型关系和等价关系, 所以可以直接用最简单的方式实现 union 和 inter, 这有些类似 schema system,比如 json 的各种 schema。

注意,我们不需要 define-class 之类的东西, 只要用 tau 就可以了。

record 正是 scheme 所最欠缺的数据类型, 这也是为什么 SRFI-9 有 define-record-type:

scheme
(define-record-type point
  (make-point x y)
  point?
  (x point-x)
  (y point-y point-put-y!))

define-record-type 设计的是很好, 但是这是为了克服 scheme 缺少 record 的问题, 如果重新设计语言,可以直接解决这个问题。

function predicate ​

在类型系统中的函数类型 (-> A B), 没法被实现为谓词,但是可以作为一种 debug 机制:

scheme
(claim square (-> int? int?))
(define square (lambda (x) (mul x x)))

在 x-lisp run <file> 时忽略这些 claim, 在 x-lisp debug <file> 时把这些 claim 所声明的参数谓词作用于函数的参数, 把返回谓词作用于函数的返回值。

在此之外我们当然也可以有 function? 谓词。

用 (the) 可以把一个 arrow 作用于函数, 可以理解为获得了一个带有 assertion 的函数:

scheme
(the (-> int? int?)
  (lambda (x) (mul x x)))

(define square
  (the (-> int? int?)
    (lambda (x) (mul x x))))