如何在 Clojure 中在运行时序列化函数?

How can I serialize functions at runtime in Clojure?

有没有办法在 Clojure 中在 运行 时间序列化函数?我希望能够以序列化格式通过网络发送无状态(但不是纯)函数(可能是 edn,但我对任何事情都持开放态度)。


例如...

如果我 运行 prn-str 一个函数,我不明白我 expected/wanted.

user=> (def fn1 (fn [x] (* x 2)))
#'user/fn1
user=> (def data {:test 1 :key "value"})
#'user/data
user=> (defn fn2 [x] (* x 2))
#'user/fn2
user=> (prn-str fn1)
"#object[user$fn1 0x28b9c6e2 \"user$fn1@28b9c6e2\"]\n"
user=> (prn-str data)
"{:test 1, :key \"value\"}\n"
user=> (prn-str fn2)
"#object[user$fn2 0x206c48f5 \"user$fn2@206c48f5\"]\n"
user=> 

我会 wanted/expected 像这样:

user=> (prn-str fn2)
"(fn [x] (* x 2))\n"

或者,也许,

user=> (prn-str fn2)
"(defn fn2 [x] (* x 2))\n"

您必须使用 quote' 来阻止求值,并使用 eval 来强制求值:

(def fn1 '(fn [x] (* x 2)))
(prn-str fn1) ;;=> "(fn [x] (* x 2))\n"
((eval fn1) 1) ;;=> 2

你可以使用 clojure.repl/source.

(with-out-str (source filter))

获取字符串,或者

(read-string (with-out-str (source filter)))

获取 clojure 列表。

你基本上有两个选择:

  • 传递源代码(s 表达式存储为 clojure 数据)
  • 传递jar文件并在另一端加载它们。

对于第一个选项,您在编译函数时保存源(几乎总是在定义时),然后将相同的源表达式传递给另一台计算机,然后让它编译同样的东西。所以首先你可以制作一个表达式向量:

(domain-functions '[(defn foo [x] x)
                    (defn bar [y] (inc y)]

然后你可以将其存储到数据库中,每个客户端都可以将其传递给read,然后它们将具有相同的功能。

第二个选项取决于这样一个事实,即每次定义一个函数时,它都会在 /target 目录中生成一个 class 文件,然后加载它。然后您可以同步此目录并将它们加载到另一侧。这种方法当然是完全疯狂的,尽管这里的人们做着疯狂的事情。我推荐第一种方法


作为个人笔记:

我现在用 datomic 做这个,我采用了使用宏将 git-hash 放入函数名称的做法,所以我绝对确定当我调用函数时,我得到了与在编辑器中看到的相同的功能。当 运行 许多实例都来自同一个数据库时,这会让您高枕无忧。

确实没有什么好方法,并且有充分的理由简单地将一个函数传送到另一台计算机或将其存储在数据库中会导致很多问题,其中最重要的是该函数可能需要其他函数那不在另一端。

一个更好的主意是坚持使用数据。不要编写函数,而是将函数名称写为事件,然后甚至可以在以后由读取数据的任何内容进行翻译。坚持使用数据,这是惯用的方式。

Flambo 是 Spark 的 Clojure 包装器,它使用实现 Java 接口 Serializable 的 serializable-fn library to serialize functions (which Spark requires). Sparkling, another wrapper for Spark, uses native Clojure functions through this Java abstract class

在某些时候它不再是 Clojure,因此我们可以任意往返于源代码和机器指令并返回的期望有点偏离。

我们应该能够将函数序列化为字节数组并通过网络发送。我怀疑您需要获取函数的 java.lang.Class 对象,然后通过远程 jvm 上的 java.lang.instrument.ClassFileTransformer to get the bytes. Once you have those you can pass them through to the friendly java.lang.ClassLoader 传递它。