2

我正在尝试使用来自另一个库的实用程序函数来建议一个库中的许多方法(defn),其中一些要建议的方法是用定义的,而一些是用(defprotocol).

现在我正在使用这个库,它使用(alter-var-root). 我不在乎我使用哪个库(或者我是否自己动手)。

我现在遇到的问题是协议方法有时可以建议,有时不能,这取决于我不完全清楚的因素。

  1. 如果我定义一个协议,然后定义一个类型并内联实现该协议,那么建议似乎永远不会起作用。我假设这是因为该类型直接扩展了 JVM 接口并跳过了 var。

  2. 如果在单个命名空间中,我定义了一个协议,然后通知它的方法,然后将该协议扩展到一个类型,那么通知将不起作用

  3. 如果在单个命名空间中,我定义了一个协议,然后将该协议扩展为一个类型,然后建议该协议的方法,则该建议起作用。

我想做的是找到一种可靠地工作并且不依赖于未定义的实现细节的建议方法。这可能吗?

4

1 回答 1

2

Clojure 本身并没有以可靠的方式为建议函数提供任何可能性,即使是通过def/定义的那些defn。考虑以下示例:

(require '[richelieu.core :as advice])

(advice/defadvice add-one [f x] (inc (f x)))

(defn func-1 [x] x)
(def func-2 func-1)

(advice/advise-var #'func-1 add-one)

> (func-1 0)
1

> (func-2 0)
0

在评估 form 之后(def func-2 func-1), varfunc-2将包含var的绑定func-1(换句话说,它的值),因此advice-var不会影响它。

尽管这样的定义func-2很少见,但您可能已经注意到或使用了以下内容:

(defn generic-function [generic-parameter x y z]
  ...)

(def specific-function-1 (partial generic-function <specific-arg-1>))
(def specific-function-2 (partial generic-function <specific-arg-2>))
...

如果您提出建议generic-function,由于上述特殊性,任何特定功能都不会按预期工作。

如果建议对您很重要,作为一种可行的解决方案,我假设以下内容:由于 Clojure 函数被编译为 java 类,您可以尝试将 java 方法替换 invoke为具有所需行为的其他方法(但是,事情变得更多谈论替换协议/接口方法时很复杂:似乎您必须在每个实现特定协议/接口的类中替换所需的方法)。

否则,您将需要为您想要建议的每个函数提供显式包装器。在这种情况下,宏可能有助于减少样板文件。

于 2016-08-17T17:22:23.517 回答