是否有可能分解Clojure函数?

时间:2013-05-23 15:34:52

标签: clojure homoiconicity

虽然我可能错误地解释了同质性的概念,但我将其理解为“代码是数据”。

所以,我可以写这样的代码:

(def subject "world")
(def helo '(str "Hello " subject))

此时,helo只是数据,但可以像下面这样执行代码:

(eval helo)

返回“Hello world”。

我还可以继续将helo视为数据:

(first helo)
(count helo)

分别返回str3

到目前为止一切顺利。但是,只要我将代码包装在函数中,我似乎就失去了将代码视为数据的能力:

(defn helofn [subject]
  (str "Hello " subject))

如何分解helofn?我似乎无法将其视为数据;如果我这样做:

(count helofn)

我得到一个例外:

  

java.lang.UnsupportedOperationException:此类型不支持count:user $ helofn

还有另一种分解helofn的方法,还是我只是期望同性恋过多?

4 个答案:

答案 0 :(得分:9)

helofn定义数据,但您要对其进行评估(就像您明确评估helo列表一样)。如果您以与helo相同的方式处理定义,那么它将保留数据,并且适用于您要应用的任何转换:

(def helofndata '(defn helofn [subject]
                   (str "Hello " subject))

=> (second helofndata)
helofn
=> (eval helofndata)
#'user/helofn

答案 1 :(得分:6)

defn只是macro

(macroexpand '(defn helofn [subject]
  (str "Hello " subject)))

(def helofn (clojure.core/fn ([subject] (str "Hello " subject))))

如果您按照定义helofn的方式定义helo,则可以将其视为数据:

(def helofn '(fn [subject]
  (str "Hello " subject)))

现在您可以评估并调用此函数:

((eval helofn) "world")

并将其视为数据:

(count helofn)

但是,当您使用defn宏时,您将helofn变量与已编译的函数关联,而不是与其代码关联。

这不仅仅是功能。假设您使用以下代码定义hello

(def helo (str "Hello " subject))

现在hello与“Hello world”字符串相关联,而不是(str "Hello " subject)代码。所以,现在没有办法获得这个字符串构建的代码。

N.B。如果您想将clojure代码视为数据,请查看其macros。传递给宏的任何代码都被视为数据,宏返回的任何数据都被视为代码。

答案 2 :(得分:3)

同质性是一个非常强大的概念,我认为你不会期待它太多。

defn实际上是一个使用def特殊形式定义函数的宏,因此:

(defn sq [x]
  (* x x))

实际上相当于:

(def sq (fn ([x] (* x x))))

因此defn接收args sq [x] (* x x),然后构建列表(def sq (fn ([x] (* x x)))),将其作为宏的结果返回,然后进行评估。这一切都是通过defn宏来处理列表,地图,矢量,符号等来完成的。

事实上,在Clojure中你无法获得你定义函数的原始符号列表,这与在Clojure中编译所有代码的事实有关。这就是为什么在REPL中评估(fn [x] 1)会返回#<user$eval809$fn__810 user$eval809$fn__810@10287d> 之类的内容。但是,正如previous answer中所提到的,评估的代码是数据。

也许我对此已经走得太远了,但是如果你想要为你定义的每个函数创建它的数据,你可以通过创建自己的自定义宏将它添加到它的元数据中。

以下是这种宏的简单实现:

(defmacro defn* [x & body ]
  (let [form `'~&form
        x    (vary-meta x assoc :form form)]
    `(defn ~x ~@body)))
;=> #'user/defn*

(defn* sq [x]
  (* x x))
;=> #'user/sq

(:form (meta #'sq))
;=> (defn* sq [x] (* x x))

&form是一个隐式参数(和&amp; env一起),它包含调用宏的整个(未评估的)形式(即编译器评估的数据)。

希望这会有所帮助,而且不会带来更多混乱。

答案 3 :(得分:1)

看起来没有基于

get a clojure function's code

Can you get the "code as data" of a loaded function in Clojure?

基本上你可以从.clj文件中定义的函数中获取源代码,但是没有可靠的方法来检索单独从函数构建函数的数据结构。

编辑:我认为你对同性恋的期望过高。代码本身是数据是,但是根据该代码发出的工件无法检索原始源代码是相当标准的。就像我有2时一样,我无法知道它是由(+ 1 1)或( - 4 2)以同样的方式产生的,函数是通过调用fn来创建的一些数据,这些数据结构被解释作为代码。

相关问题