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

时间:2019-01-17 00:02:04

标签: clojurescript

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

(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被本地遮蔽。我不想重写功能,只是调用它。不知道我要去哪里错了。我看了几个例子,但看不出来。

2 个答案:

答案 0 :(得分:2)

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

因此,ClojureScript的resolve编译时工具(一个需要用文字引号引起来的宏)。

如果您使用:simple或自托管的ClojureScript,则可以使用更多选项,因为所需的支持将持续到运行时。例如,Planck具有一个planck.core/resolve,其行为类似于Clojure的resolve。在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来构建更大的地图。

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

如果您需要进一步最小化生成的代码大小,则可以像在

中那样构建/维护一个从字符串到函数值的简单静态映射。
(def fns {"square" foo.core/square})

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

另一种选择是标记您需要使用^:export元访问的功能,然后使用JavaScript interop调用这些功能。例如,如果您以这种方式定义函数

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

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

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

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

请注意,没有通用的解决方案可以让您在运行时在:advanced下按名称查找函数,而又不希望在编译时将该函数的某些方面放入代码中。 (实际上,如果未以Google Closure Compiler可以静态方式引用的函数,则该函数的实现将被完全删除为无效代码。)在上面,ns-publics捕获名称空间的所有var在编译时,滚动您自己的查找映射会设置静态代码以引用该函数值,并使用^:export进行静态安排以使该函数的名称持久到运行时。

答案 1 :(得分:0)

您需要像这样使用它:

((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!