【问题标题】:In common-lisp how can i override/change evaluation behaviour for a specific type of object?在 common-lisp 中,我如何覆盖/更改特定类型对象的评估行为?
【发布时间】:2011-06-02 10:43:24
【问题描述】:

在 common-lisp 中,我想实现一种这样的参考系统:

假设我有:

(defclass reference () ((host) (port) (file)))

我也有:

(defun fetch-remote-value (reference) ...) 获取并反序列化一个 lisp 对象。

我如何干预评估过程,以便在评估引用对象时,获取远程值并再次重新评估以产生最终结果?

编辑:

更详细地描述我想要完成的工作:

使用 cl-store 我序列化 lisp 对象并将它们发送到要保存的远程文件(或 db 或任何东西)。成功存储后,我将主机、端口和文件保存在参考对象中。我想,每当在引用对象上调用 eval 时,首先检索该对象,然后对检索到的值调用 eval。由于引用也可以在其他(父)对象或聚合类型中序列化,因此我可以通过修改 eval 获得免费的递归远程引用解析,因此我不必自己遍历和解析加载对象的子引用。

编辑: 由于对象总是对自己进行评估,所以我的问题有点错误。基本上我想做的是:

我想拦截符号的评估,以便当它们的值是 REFERENCE 类型的对象时,而不是将对象作为符号评估的结果返回,而是返回 (fetch-remote-value object) 的结果?

【问题讨论】:

    标签: lisp common-lisp eval


    【解决方案1】:

    简而言之:你不能这样做,除非重写函数 eval 和修改你的 Lisp 的编译器。评估规则是固定的 Lisp 标准。

    编辑在阅读了增强的问题后,我认为您无法在此处实现参考的完全透明。在这样的场景中

    (defclass foo () (reference :accessor ref))
    (ref some-foo)
    

    调用 ref 的结果只是一个值;无论其类型如何,都不会考虑对其进行评估。

    当然,您可以通过某种方式定义访问器,从而透明地进行解析:

    (defmacro defresolver (name class slot)
        `(defmethod ,name ((inst ,class))
            (fetch-remote-reference (slot-value inst ',slot))))
    
    (defresolver foo-reference foo reference)
    

    编辑您可以(在某种程度上)使用符号宏连接到 Common Lisp 的符号解析机制:

    (defmacro let-with-resolution (bindings &body body) 
        `(symbol-macrolet ,(mapcar #'(lambda (form) (list (car form) `(fetch-aux ,(cadr form)))) bindings) ,@body))
    
    (defmethod fetch-aux ((any t)) any)
    (defmethod fetch-aux ((any reference)) (fetch-remote-reference any))
    

    然而,现在事情变得相当神秘了;并且变量不再是变量,而是神奇的符号,只是看起来像变量。例如,无法通过此宏修改“绑定”变量的内容。使用这种方法可以做的最好的事情是为fetch-aux 提供一个setf 扩展,它会修改原始位置。

    【讨论】:

    • 你能详细说明一下普通的闭包解决方案吗?
    • 我在问题中添加了更详细的问题描述
    • "调用 ref 的结果只是一个值;不管它的类型如何,它都不会被考虑评估" :当然,但是如果我们可以将它放在 eval 过程中,那么如果有人将该值绑定到 var 或隐式调用它,它将由评估器自动获取并解析。无论如何,您的建议非常有帮助,但我希望有一种方法可以在评估的徒劳中注入对引用类型的特殊处理。
    【解决方案2】:

    尽管lazy evaluationeobject persistence 的库为您提供了部分帮助,但Common Lisp 并没有提供一种可移植的方式来实现完全透明的持久值。惰性值或持久值仍必须显式为 forced

    MOP 可用于实现惰性或持久对象,但槽值是透明强制的。需要对 Common Lisp 实现的内部进行更改以提供一般透明度,因此您可以这样做,例如(+ p 5)p 可能持有持久或惰性值。

    【讨论】:

    • 我想以更抽象的方式来看待它:在 (+ p 5) 中,p 可以是对任意事物的引用,因此它的值可以是内存地址中的任何值或方案获取的资源。 Lazy 或 Eager 与引用或表达式的值所在的位置正交。 Lazy 或 eager 定义 WHEN 来获取表达式或引用的值。我希望能够控制从哪里获取表达式的值,或者通过以某种方式专门对引用类型上的表达式求值,或者截取它们的结果。
    • 我知道该语言不提供完全透明的持久值的可能性。我只是想知道: 1.更改实现以使其支持完全透明和解耦的值持久性有多困难。 2.值得吗?我的意思是从这样的事情中受益到可以证明这种尝试是合理的程度吗?
    【解决方案3】:

    无法直接更改评估机制。你需要为你的代码编写一个编译器来编译其他东西。一种嵌入式语言。

    在 CLOS 层面有几种处理方式:

    两个例子:

    • 编写在引用对象上调度的函数:

      (defmethod move ((对象引用)位置) (移动(解除引用)位置))

      (defmethod move ((对象汽车)位置) ...))

    这会变得很难看,可能会通过宏自动执行。

    CLOS 对象已经具有间接性,因为它们可以更改其类。即使他们可能改变他们的班级,他们仍然保持他们的身份。 CHANGE-CLASS 正在破坏性地修改实例。

    这样就可以传递引用对象并在某些时候加载数据,将引用对象更改为其他类并相应地设置插槽。这种更改类需要在代码中的某处触发。

    自动触发它的一种方法可能是使用错误处理程序来捕获涉及引用对象的某些类型的错误。

    【讨论】:

      【解决方案4】:

      我会在您的反序列化机制之上添加一个层,该层会根据传入数据的类型进行调度。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-29
        • 1970-01-01
        • 1970-01-01
        • 2018-09-03
        相关资源
        最近更新 更多