如何将 ClojureScript 函数序列化为 edn,然后反序列化并调用它们?

How to serialize ClojureScript functions to edn and then later deserialize and invoke them?

如果你像这样构造一个字符串

(def s (pr-str {:greet '(partial str "Hello" " " "World!")}))

如何使用 reader(即 read-string)读取结构并将 :greet 键的值作为可以调用的函数拉回?

请注意,通过引用代码可以保留形状。如果我删除引号,它会序列化底层 javascript 函数的内容。我也尝试了反引号 (`)。

目标是能够保存用户在某些应用程序中构建的函数,将这些函数序列化为 edn,然后反序列化该文本,提取能够被调用的函数。

以上代码在调用时应该 return "Hello World!"。

安全问题可以单独解决。

您基本上可以使用相同的解决方案http://clojurescript.net/ is using - which is cljs-bootstrap使其工作。

演示:

我准备了a demo repository你可以克隆和运行。它包含一个简单的网页,其中包含您可以输入的内容,将对其进行实时评估。代码非常简短,因此应该易于理解并适应您的需求。它看起来像这样:

解决方案

第一步:得到cljs-bootstrap file compiled to JS。它包含我们将使用的 read_eval_print 方法。在加载已编译的 CLJS 文件之前,将此文件加载到 HTML 文件中。

第 2 步: 由于我们使用的是 JS,而不是正确的 CLJS 依赖项,我们可能需要 externs(即在高级编译模式下):

var cljs_bootstrap = {};
cljs_bootstrap.core = {};
cljs_bootstrap.core.read_eval_print = function() {};

记得将它们添加到您的 project.clj

第 3 步: read_eval_print 接受两个参数 - 第一个是带有 ClojureScript 代码的字符串,第二个是评估完成后将调用的回调。此代码将执行:

(let [code "(prn ((partial str \"Hello\" \"World\") \" :)\"))"
      cljs (-> js/window .-cljs_bootstrap .-core)]
  (.read_eval_print
    cljs
    code
    (fn [success _] (prn "Success?" success))))

如您所见,实际上非常简单:

  • cljs用于从window对象中获取read_eval_print方法。那是因为我们将此文件导入为纯 JS 依赖项,而不是 CLJS 依赖项(这也是我们不需要 (:require) 任何东西的原因。
  • callback 接受两个参数 - 第一个是布尔标志,如果代码有效并且可以被评估则设置为 true,否则为 false。第二个参数是一个错误。

此代码在浏览器中执行时会将其打印到 JS 控制台:

差不多就这些了。

几个笔记

  • 将 cljs-bootstrap 用作 Clojure 依赖项会很棒,可以作为 Lein 依赖项,也可以仅通过复制所需的命名空间。由于时间不够,我没有这样做。它的工作原理应该是一样的,您只需 (:require) 即可。不过没时间玩。
  • 当您加载演示页面时,它会显示一些 JS 加载错误(找不到文件)。那些其实可以忽略,CLJS 编译仍然有效。我没研究过。