通过 Slime 重新加载多种方法

发布于 2025-01-07 15:10:41 字数 343 浏览 1 评论 0原文

在使用 Slime repl 在 Emacs 中进行开发时,我在重新加载多方法时遇到了问题。

重新定义 defmethod 表单工作正常,但如果我更改调度函数,我似乎无法重新加载 defmulti 表单。我想我专门添加或删除了调度函数参数。

作为解决方法,我已经能够 ns-unmap multimethod var,重新加载 defmulti 表单,然后重新加载所有 defmethod 表单。

据推测,这是 Clojure 实现多方法的方式的“限制”,即我们为了执行速度而牺牲了一些活力,但是是否有任何习惯用法或开发实践可以帮助解决这个问题?

I'm having trouble reloading multimethods when developing in Emacs with a Slime repl.

Redefining the defmethod forms works fine, but if I change the dispatch function I don't seem to be able to reload the defmulti form. I think I specifically added or removed dispatch function parameters.

As a workaround I've been able to ns-unmap the multimethod var, reload the defmulti form, and then reload all the defmethod forms.

Presumably this is a "limitation" of the way Clojure implements multimethods, i.e. we're sacrificing some dynamism for execution speed, but are there any idioms or development practises that help workaround this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以歌曲疗慰 2025-01-14 15:10:41

简而言之,您处理此问题的方式是完全正确的。如果您发现自己更新了多方法以便特别频繁地更改调度函数,(1)我认为这很不寻常:-),(2)您可以编写一套函数/宏来帮助重新加载。我画了两个未经测试的(!)宏来帮助完成下面的(2)。

为什么?

然而,首先简要讨论“为什么”。当前实现的多方法的调度函数查找不需要同步 - 调度 fn 存储在 MultiFn 对象的 final 字段中。这当然意味着您不能只更改给定多方法的调度函数——您必须重新创建多方法本身。正如您所指出的,这需要重新注册所有先前定义的方法,这很麻烦。

当前的行为允许您重新加载带有 defmethod 表单的命名空间,而不会丢失所有方法,但代价是替换实际的多方法会稍微麻烦一些(当您确实想要这样做时)。

如果您确实愿意,可以通过反射更改调度 fn,但这存在语义问题,特别是在多线程场景中(请参阅 Java 语言规范 17.5.3 有关构造后对 final 字段进行反射更新的信息)。

黑客(非反射)

(2)的一种方法是在使用宏重新定义后自动重新添加方法(未经测试)

(defmacro redefmulti [multifn & defmulti-tail]
  `(let [mt# (methods ~multifn)]
     (ns-unmap (.ns (var ~multifn)) '~multifn)
     (defmulti ~multifn ~@defmulti-tail)
     (doseq [[dispval# meth#] mt#]
       (.addMethod ~multifn dispval# meth#))))

另一种设计将使用名为 with-method 的宏-重新注册,采用 multifn 名称和主体的序列,并承诺在执行主体后重新注册方法;这是一个草图(再次未经测试):

(defmacro with-method-reregistration [multifns & body]
  `(let [mts# (doall (zipmap ~(map (partial list 'var) multifns)
                              (map methods ~multifns))))]
     ~@body
     (doseq [[v# mt#] mts#
             [dispval# meth#] mt#]
       (.addMethod @v# dispval# meth#))))

您可以用它来表示 (with-method-reregistration [my-multi-1 my-multi-2] (require :reload 'ns1 ns2)) 。不确定这是否值得失去清晰度。

The short answer is that your way of dealing with this is exactly correct. If you find yourself updating a multimethod in order to change the dispatch function particularly frequently, (1) I think that's unusual :-), (2) you could write a suite of functions / macros to help with the reloading. I sketch two untested (!) macros to help with (2) further below.

Why?

First, however, a brief discussion of the "why". Dispatch function lookup for a multimethod as currently implemented requires no synchronization -- the dispatch fn is stored in a final field of the MultiFn object. This of course means that you cannot just change the dispatch function for a given multimethod -- you have to recreate the multimethod itself. That, as you point out, necessitates re-registration of all previously defined methods, which is a hassle.

The current behaviour lets you reload namespaces with defmethod forms in them without losing all your methods at the cost of making it slightly more cumbersome to replace the actual multimethod when that is indeed what you want to do.

If you really wanted to, the dispatch fn could be changed via reflection, but that has problematic semantics, particularly in multi-threaded scenarios (see Java Language Specification 17.5.3 for information on reflective updates to final fields after construction).

Hacks (non-reflective)

One approach to (2) would be to automate re-adding the methods after redefinition with a macro along the lines of (untested)

(defmacro redefmulti [multifn & defmulti-tail]
  `(let [mt# (methods ~multifn)]
     (ns-unmap (.ns (var ~multifn)) '~multifn)
     (defmulti ~multifn ~@defmulti-tail)
     (doseq [[dispval# meth#] mt#]
       (.addMethod ~multifn dispval# meth#))))

An alternative design would use a macro called, say, with-method-reregistration, taking a seqable of multifn names and a body and promising to reregister the methods after executing the body; here's a sketch (again, untested):

(defmacro with-method-reregistration [multifns & body]
  `(let [mts# (doall (zipmap ~(map (partial list 'var) multifns)
                              (map methods ~multifns))))]
     ~@body
     (doseq [[v# mt#] mts#
             [dispval# meth#] mt#]
       (.addMethod @v# dispval# meth#))))

You'd use it to say (with-method-reregistration [my-multi-1 my-multi-2] (require :reload 'ns1 ns2)). Not sure this is worth the loss of clarity.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文