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)) ;=>
&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.
source share