让我们来看看flet和macrolet:有没有一种方法可以做一个“ class-let”?

时间:2018-09-05 12:45:39

标签: common-lisp

我有一个宏,它在某些规则下定义了一个类,即伪代码:

(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都太长了,我宁愿选择一种解决方案,而不必为每个测试加载/卸载程序包(我不知道它是否可以工作以及何时将类垃圾回收。卸载包裹...)。

谢谢您的回答。

1 个答案:

答案 0 :(得分:2)

我不这样认为。

如何存储类的机制是完全由实现定义的,它们只需要符合MOP(至少在标准要求的范围内)。但是,MOP没有规定使类注册表动态化的任何内容。实际上,类型和类名被指定为全局环境(CLHS ch. 3.1.1.1)的一部分,因此,在这里,一致的实现很难动态化。

如您所写,一旦定义,也没有指定的方法来摆脱类。

从理论上讲,我认为如果没有此功能,将很难提供现有实现所具有的那种优化的运行时。类查找需要快速。

现在,进入元问题:您正在尝试做什么?通常,虽然代码是数据,但您不应将程序逻辑与编程逻辑混淆。您提议的内容看起来可能旨在使代码代表数据。我建议考虑干净的分离和正交表示。