Clojure/Clojurescript: 要解析的参数必须是带引号的符号

Clojure/Clojurescript: Argument to resolve must be a quoted symbol

我正在尝试使用保存在变量中的字符串来调用这样的函数:

(defn update-product-list [] "Test")

(defn handle-state-change [action]
  ((resolve (symbol action))))

(handle-state-change "update-product-list")

但是,这给了我以下错误:断言失败:要解析的参数必须是带引号的符号

我也尝试将上面的行更改为:

((resolve (quote (symbol action))))

但是这样还是报错。我也尝试将其更改为:

((resolve 'action))

但这给出了一个我不太明白的不同错误:js/action 被本地人遮蔽了。我不想覆盖函数只是调用它。不知道我哪里错了。我查看了几个示例,但无法确定。

你需要这样使用它:

((resolve 'inc)  5))  => 6

或者,稍微解构一下:

(let [the-fn (resolve 'inc)]
   (the-fn 7))

=> 8

如果函数名称为字符串,请使用 symbol 函数从字符串转换 => 符号 (from clojuredocs.org):

user=> ((-> "first" symbol resolve) [1 2 3])
1

而且,永远不要忘记 the Clojure CheatSheet!

ClojureScript 支持 :advanced 优化,其中 Google Closure 编译器将重命名、内联或删除(未使用的)函数以实现缩小。简而言之,您要查找的函数的名称通常不会再存在于 :advanced.

因此,ClojureScript 的 resolve 是一个 编译时 设施(一个需要文字引号的宏)。

如果您使用 :simple 或自托管的 ClojureScript,您可以使用更多选项,因为所需的支持会持续到运行时。例如Planck has a planck.core/resolve that behave's like Clojure's resolve. A similar approach is possible in Lumo,如果使用:simple.

也可以制作类似的设施

一般来说,给定 :advanced,如果您需要将字符串映射到一组函数,则需要以某种方式安排在编译时构造静态映射以支持此(函数集必须在编译时先验知道。

如果您有一个命名空间(其名称在编译时静态已知)定义了需要通过字符串动态调用的函数,您可以考虑使用 ns-publics:

cljs.user=> (ns foo.core)

foo.core=> (defn square [x] (* x x))
#'foo.core/square
foo.core=> (in-ns 'cljs.user)
nil
cljs.user=> (when-some [fn-var ((ns-publics 'foo.core) (symbol "square"))]
               (fn-var 3))
9

这将在 :advanced 下工作。 ns-publics构造的映射是静态的;在编译时构建。如果您有多个需要此类处理的名称空间,您可以 merge 多次调用 ns-publics 来构建更大的地图。

这种方法的优点是所涉及的代码非常短,几乎不需要维护。缺点是它会将命名空间的所有 public 变量(在本例中为 foo.core)转储到您生成的代码中(并且为变量生成的代码有些冗长)。另一个缺点是您需要在编译时静态知道涉及的名称空间。

如果您需要进一步减少生成的代码大小,您可以构建/维护一个从字符串到函数值的简单静态映射,如

(def fns {"square" foo.core/square})

并适当地使用它,随着代码库的发展使其保持最新。

另一种选择是使用 ^:export 元标记您需要访问的函数,然后使用 JavaScript 互操作来调用这些函数。例如,如果你这样定义函数

 (defn ^:export square [x] (* x x))

然后您可以使用字符串/互操作来查找函数并在运行时调用它。这是一个例子:

 ((goog.object/getValueByKeys js/window #js ["foo" "core" "square"]) 3)

涵盖 ^:export:advanced 的使用 here。如果您知道您正在使用 :simple 或更少,那么您可以简单地使用 JavaScript 互操作来调用感兴趣的函数,而无需使用 ^:export.

请注意,没有通用的解决方案可以让您在运行时按名称在 :advanced 下查找函数,而无需在编译时以某种方式将该函数的某些方面放入您的代码中。 (事实上​​ ,如果一个函数没有以 Google Closure Compiler 可以静态引用的方式引用,看,函数实现将作为死代码完全消除。)在上面,ns-publics 抓取所有vars 在编译时命名空间,滚动你自己的查找映射设置静态代码来引用函数值,并使用 ^:export 静态安排使函数名称持续到运行时。