You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Common Lisp类定义可用于typecase及相关表达式的类型?

Common Lisp中类与类型的关联及自定义字符串类建模问题

一、自定义类在typecase中无法直接使用的问题

我定义了如下自定义类:

(defclass my-string ()
  ((data :initarg :data :type simple-string)
   (properties :initarg :properties :type interval-tree))
  (:documentation
   "Represents a string with properties."))

尝试在typecase中把my-string作为类型使用时,编译报错:

(defun upcase (obj)
  (typecase obj
    (my-string (string-upcase (slot-value obj 'data)))
    (string (string-upcase obj))
    (integer (char-upcase (code-char obj)))
    (character (char-upcase obj))
    (otherwise (error "Wrong type argument ~S" obj))))

我错误地尝试用deftype声明类型:

(deftype my-string (s) (typep s 'my-string))

使用时必须写成(my-string obj),但SBCL标记该分支为不可达:

(defun upcase (obj)
  (typecase obj
    ((my-string obj) (string-upcase (slot-value obj 'data)))
    (string (string-upcase obj))
    (integer (char-upcase (code-char obj)))
    (character (char-upcase obj))
    (otherwise (error "Wrong type argument ~S" obj))))

二、结构体的对比:自动生成对应类型

改用defstruct时,初始的typecase代码可以正常运行:

(defstruct my-string
  data properties)

展开宏后看到生成了谓词函数:

(SB-C:XDEFUN MY-STRING-P
     :PREDICATE
     NIL
     (SB-KERNEL::OBJECT)
   (TYPEP SB-KERNEL::OBJECT 'MY-STRING))

疑惑结构体是如何内部关联类型,而自定义类无法直接做到这点?

三、相关问题:如何建模扩展string的自定义类

是像my-string这样用组合方式(内置字符串作为槽)更好,还是继承自内置string类?已知继承不是最佳方式,想了解正确的建模逻辑,同时深入理解Common Lisp中类型与类的关系。


解决方案与原理

1. 正确让自定义类在类型表达式中生效

Common Lisp中类本身确实对应一个类型,但问题出在编译时可见性:defclass默认是运行时定义类,若编译typecase函数时类还未被加载,编译器无法识别该类型。解决方法是用eval-when确保类在编译、加载、运行阶段都可见:

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defclass my-string ()
    ((data :initarg :data :type simple-string)
     (properties :initarg :properties :type interval-tree))
    (:documentation "Represents a string with properties.")))

之后即可直接在typecase中使用my-string作为类型,无需额外deftype声明:

(defun upcase (obj)
  (typecase obj
    (my-string (string-upcase (slot-value obj 'data)))
    (string (string-upcase obj))
    (integer (char-upcase (code-char obj)))
    (character (char-upcase obj))
    (otherwise (error "Wrong type argument ~S" obj))))

2. 结构体与类的类型关联差异

defstruct会在编译阶段自动完成以下操作:

  • 注册对应的类型标识符
  • 生成同名谓词函数(如my-string-p)
  • 让编译器直接识别该类型

而defclass是动态的,默认仅在运行时创建类,需要通过eval-when显式告知编译器提前处理类定义,才能在类型检查中生效。

3. 扩展string的最佳建模方式

优先选择组合而非继承,原因如下:

  • 内置string属于数组的特殊子类,继承自它的自定义类无法被原生字符串操作函数(如string-upcase)直接识别,需为每个函数单独定义方法,维护成本极高。
  • 组合方式(用槽存储原生字符串)逻辑清晰:可明确区分对内部字符串的操作和对扩展属性的操作,灵活扩展功能的同时复用原生字符串的所有工具函数。

类型与类的核心关系

  • 类是面向对象系统的核心,用于实例化对象和方法调度;类型则用于类型检查、编译优化和声明。
  • 每个类对应一个类型,但类型系统还包含大量非类类型(如(integer 0 10)、satisfies谓词类型等)。
  • 用户自定义类只要保证编译时可见,就能直接在typecase、typep等类型操作中使用类名。

内容的提问来源于stack exchange,提问作者myname

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 14:53:18