Search code examples
clojurehomoiconicity

Is it possible to decompose a Clojure function?


While I may incorrectly interpret the concept of homoiconicity, I've understood it as 'code being 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 like this:

(eval helo)

which returns "Hello world".

I can also continue to treat helo as data:

(first helo)
(count helo)

which returns respectively str and 3.

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

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

How do I decompose helofn? It seems that I can't treat it as data; if I do this:

(count helofn)

I get an exception:

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

Is there another way to decompose helofn, or am I just expecting too much from homoiconicity?


Solution

  • defn is just a macro:

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

    If you define helofn the way you defined helo, you'll be able to treat it as data:

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

    Now you can eval and call this function:

    ((eval helofn) "world")
    

    and to treat it as a data:

    (count helofn)
    

    But, when you use defn macro you associates helofn variable with compiled function and not with it's code.

    It's not just functions. Let's say you defined hello with the following code:

    (def helo (str "Hello " subject))
    

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

    N.B. If you want to treat clojure code as data you should look into its macros. Any code passed to a macro is treated as data and any data returned by a macro is treated as code.