虽然我可能错误地解释了同质性的概念,但我将其理解为“代码是数据”。
所以,我可以写这样的代码:
(def subject "world")
(def helo '(str "Hello " subject))
此时,helo
只是数据,但可以像下面这样执行代码:
(eval helo)
返回“Hello world”。
我还可以继续将helo
视为数据:
(first helo)
(count helo)
分别返回str
和3
。
到目前为止一切顺利。但是,只要我将代码包装在函数中,我似乎就失去了将代码视为数据的能力:
(defn helofn [subject]
(str "Hello " subject))
如何分解helofn
?我似乎无法将其视为数据;如果我这样做:
(count helofn)
我得到一个例外:
java.lang.UnsupportedOperationException:此类型不支持count:user $ helofn
还有另一种分解helofn
的方法,还是我只是期望同性恋过多?
答案 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)
看起来没有基于
和
Can you get the "code as data" of a loaded function in Clojure?
基本上你可以从.clj文件中定义的函数中获取源代码,但是没有可靠的方法来检索单独从函数构建函数的数据结构。
编辑:我认为你对同性恋的期望过高。代码本身是数据是,但是根据该代码发出的工件无法检索原始源代码是相当标准的。就像我有2时一样,我无法知道它是由(+ 1 1)或( - 4 2)以同样的方式产生的,函数是通过调用fn来创建的一些数据,这些数据结构被解释作为代码。