是否可以在 Common Lisp 中定义递归类型?

Is it possible to define a recursive type in Common Lisp?

递归类型是一种具有基础和自身递归情况的类型。

我希望它能实现 "typed lists",即其 conses 只允许相同元素类型或 nil 的列表。

我尝试了以下定义:

(deftype list-of (a) `(or null
                          (cons ,a (list-of ,a))))

但是,由于编译器试图无限期地递归 list-of,这表明存在堆栈耗尽问题(至少在 SBCL 上)。是否可以定义这样的数据类型?

是的,但我作弊了 ;)

(defun satisfication (a)
  (if a
      (and (integerp (car a))
       (satisfication (cdr a)))
      T))

(deftype my-list () `(satisfies satisfication))


(typep (cons 1 (cons 2 (cons 3 nil))) 'my-list)
> T


(typep (cons 1 (cons 2 (cons 3.2 nil))) 'my-list)
> NIL

显然 SBCL 不喜欢递归类型 - 另一个答案很好地解释了原因。但是,如果您想坚持标准并仍然定义递归类型,那么隧道尽头有一线曙光:您可以定义任何函数来检查满意度。

这不可能。您使用 DEFTYPE 定义的类型是 "derived types"。派生类型被扩展(如宏)为 "real" 类型说明符,它不能包含派生类型。扩展中对派生类型(类型本身或其他类型)的所有引用也被扩展。这样递归类型就会进入无限循环去尝试展开。

Trivial Types 为专有列表提供了一种类型,但尽管将其作为参数,但实际上并不检查元素类型。出于美观原因,这就足够了。

(ql:quickload :trivial-types)
(use-package :trivial-types)
(typep '("qwe" "asd" "zxc") '(proper-list string)) ;=> T
(typep '("qwe" "asd" "zxc" 12) '(proper-list string)) ;=> T

否则,您可以定义一个类型来检查前几个元素的类型是否正确。这至少会捕捉到最明显的违规行为。

(deftype list-of (a)
  `(or null (cons ,a (or null (cons ,a (or null (cons ,a list)))))))
(typep '("asd") '(list-of string)) ;=> T
(typep '("asd" 12) '(list-of string)) ;=> NIL
(typep '("asd" "qwe") '(list-of string)) ;=> T
(typep '("asd" "qwe" 12) '(list-of string)) ;=> NIL
(typep '("asd" "qwe" "zxc") '(list-of string)) ;=> T
(typep '("asd" "qwe" "zxc" 12) '(list-of string)) ;=> T

如果您希望它适用于任何长度的列表,则必须为您需要的每个不同列表定义一个类型。

(defun list-of-strings-p (list)
  (every #'stringp list))
(deftype list-of-strings ()
  `(or null (satisfies list-of-strings-p)))
(typep '("qwe" "asd" "zxc" "rty" "fgh") 'list-of-strings) ;=> T
(typep '("qwe" "asd" "zxc" "rty" "fgh" 12) 'list-of-strings) ;=> NIL