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
静态安排使函数名称持续到运行时。
我正在尝试使用保存在变量中的字符串来调用这样的函数:
(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
静态安排使函数名称持续到运行时。