我有一个宏,它在某些规则下定义了一个类,即伪代码:
(defvar *all-my-classes* nil)
(defmacro my-macro (param)
`(if ,param
(progn
(defclass class-A () ...)
(push class-A *all-my-classes*))
(progn
(defclass class-B () ...)
(push class-B *all-my-classes*))))
我想测试宏的行为。 Let是模拟变量的便捷工具。如果我正在运行*all-my-classes*
的实例,则只需要执行以下操作即可:
(let ((*all-my-classes* my-new-value)) ; generally `nil` for the test
(my-macro false))
但是我想保留*all-my-classes*
与定义的类之间的对应关系。因为我要测试所有情况,所以让我们假设class-A
是在当前环境中定义的,并且我想测试运行(my-macro false)
是否正确定义了class-B
。
由于这只是一个测试,因此我希望该测试断言class-B
当前已定义,而class-A
在当前本地环境中未定义;那么当测试结束时,class-B
在全局环境中是未定义的,并且class-A
仍然是定义的(没有任何更改)。
这种方式最适合我使用:
(let ((*all-my-classes* nil))
(class-let ((class-A nil) ; or a way to map to a pre-defined
(class-B nil)) ; empty class temporarily.
(my-macro false)
(and
;; assert that the class is added to the list
(eql (length *all-my-classes*) 1)
;; assert that class-A is not defined
(null (find-class 'class-A))
;; assert that class-B is defined
(find-class 'class-B))))
我一直在寻找是否可以取消定义一个类,但是它看起来很复杂且依赖于实现。我想保留当前环境。
每次为每个测试重新启动LISP都太长了,我宁愿选择一种解决方案,而不必为每个测试加载/卸载程序包(我不知道它是否可以工作以及何时将类垃圾回收。卸载包裹...)。
谢谢您的回答。
答案 0 :(得分:2)
我不这样认为。
如何存储类的机制是完全由实现定义的,它们只需要符合MOP(至少在标准要求的范围内)。但是,MOP没有规定使类注册表动态化的任何内容。实际上,类型和类名被指定为全局环境(CLHS ch. 3.1.1.1)的一部分,因此,在这里,一致的实现很难动态化。
如您所写,一旦定义,也没有指定的方法来摆脱类。
从理论上讲,我认为如果没有此功能,将很难提供现有实现所具有的那种优化的运行时。类查找需要快速。
现在,进入元问题:您正在尝试做什么?通常,虽然代码是数据,但您不应将程序逻辑与编程逻辑混淆。您提议的内容看起来可能旨在使代码代表数据。我建议考虑干净的分离和正交表示。