Is it possible to decompose the Clojure function?

Although I may misinterpret the concept of homoconicity, I understood it as "code that is data."

So, I can write code like this:

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

At this point, helo is only data, but can be executed as code:

 (eval helo) 

which returns "Hello world".

I can also continue to treat helo as data:

 (first helo) (count helo) 

which returns str and 3 respectively.

So far so good. However, as soon as I complete the code in a function, I seem to lose my ability to treat code as data:

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

How to decompose helofn ? It seems that I cannot consider it as data; if i do this:

 (count helofn) 

I get an exception:

java.lang.UnsupportedOperationException: count is not supported for this type: user $ helofn

Is there any other way to decompose helofn , or am I just expecting too much from homoconicity?

+6
source share
4 answers

defn is just macro :

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

If you define helofn way you defined helo , you can treat it as data:

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

Now you can eval and call this function:

 ((eval helofn) "world") 

and consider it as data:

 (count helofn) 

But, when you use the defn macro, you bind the helofn variable to the compiled function, not to it.

These are not just functions. Say you defined hello with the following code:

 (def helo (str "Hello " subject)) 

Now hello is associated with the string "Hello world", and not with the code (str "Hello " subject) . So, now there is no way to get the code this line was built with.

NB If you want to treat clojure code as data, you should study its macros . Any code passed to the macro is treated as data, and any data returned by the macro is treated as code.

+6
source

The definition of helofn is data, but you give it the opportunity to evaluate (just like you explicitly rated the helo list). If you viewed the definition in the same way as helo , then it will remain given and will succumb to any transformations that you want to apply:

 (def helofndata '(defn helofn [subject] (str "Hello " subject)) => (second helofndata) helofn => (eval helofndata) #'user/helofn 
+9
source

Homoiconicity is a very powerful concept, and I don’t think you expect too much from it.

defn is actually a macro that uses a special form of def to define a function, therefore:

 (defn sq [x] (* xx)) 

Actually equivalent to:

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

So, defn here get args sq [x] (* xx) , then builds a list (def sq (fn ([x] (* xx)))) , returns it as a result of the macro, and then evaluates it. All this is done by manipulating lists, maps, vectors, symbols, etc. Macro defn .

The fact that in Clojure you cannot get the original list of characters from which you defined the function is related to the fact that in Clojure all the code is compiled. This is why evaluating (fn [x] 1) in REPL returns something like #<user$eval809$fn__810 user$eval809$fn__810@10287d > . But still, as mentioned in the previous answer , the code that is being evaluated is data.

I may have gone too far, but if you want for each function you define the data from which it was created, you could add it to your metadata by creating your own macro.

Here's a naive implementation for such a macro:

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

&form is an implicit argument (along with env) that contains the entire (invaluable) form with which the macro was called (i.e. data that is evaluated by the compiler).

Hope this helps, and it does not bring more confusion.

+3
source

It seems that based

get clojure function code

and

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

Basically, you can get the source from the function defined in the .clj file, but there is no reliable way to get the data structures that built the function only from this function.

EDIT: Also, I think you expect too much from homoiconicity. The code itself is yes, but it's pretty standard, so as not to get the source code based on the artifact emitted by this code. For example, when I have 2, I cannot understand that it was created (+ 1 1) or (- 4 2) in the same way that a function is a piece of data created by calling fn on some other data structures that are interpreted like a code.

+1
source

Source: https://habr.com/ru/post/945693/


All Articles