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

时间:2016-08-03 21:29:35

标签: function serialization clojure edn

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

例如......

如果我在一个函数上运行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=> ,我就得不到我想要/想要的东西。

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

我希望/期望这样的事情:

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

或者,也许,

FrameModel

6 个答案:

答案 0 :(得分:3)

您必须使用quote'来阻止评估并eval强制进行评估:

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

答案 1 :(得分:2)

您基本上有两种选择:

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

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

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

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

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

<小时/> 并作为个人说明:

我现在正在使用datomic执行此操作,并且我采用了将git-hash放入函数名称中使用宏的做法,因此我绝对清楚地知道当我调用函数时,我得到了相同的结果我在编辑器中看到的功能。当运行所有来自同一个DB的实例时,这会让您高枕无忧。

答案 2 :(得分:2)

Flambo是Spark的Clojure包装器,它使用serializable-fn库来序列化函数(Spark需要这些函数)。 Sparkling是Spark的另一个包装器,它通过实现Java接口Serializable的this Java abstract class使用本机Clojure函数。

答案 3 :(得分:1)

您可以使用clojure.repl/source

(with-out-str (source filter))

获取字符串,或

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

获取一个clojure列表。

答案 4 :(得分:1)

确实没有好的方法,并且有充分的理由只是将一个功能发送到另一台计算机或将其存储在DB中会导致很多问题,其中最重要的是该功能可能需要其他功能。在另一端。

更好的想法是坚持使用数据。而不是编写函数,将函数的名称写为事件,然后甚至可以通过读取数据的任何内容来翻译。坚持使用数据,这是惯用的方式。

答案 5 :(得分:1)

在某些时候它不再是Clojure,所以我们可以随意地从源头到机器指令再往返一趟的期望有点偏离。

我们应该能够将函数序列化为字节数组并通过线路发送。我怀疑你需要获取函数的java.lang.Class对象,然后通过java.lang.instrument.ClassFileTransformer传递它来获取字节。一旦你有了那些,你可以将它们传递给远程jvm上的友好java.lang.ClassLoader