简体   繁体   中英

How exactly does Clojure process function definitions?

I'm studying Clojure, and I've read that in Clojure a function definition is just data, ie parameters vector is just an ordinary vector. If that's the case, why can I do this

(def add (fn [a b]
  (+ a b)))

but not this

(def vector-of-symbols [a b])

?

I know I normally would have to escape symbols like this:

(def vector-of-symbols [`a `b])

but why don't I have to do it in fn / defn ? I assume this is due to fn / defn being macros. I tried examining their source, but they are too advanced for me so far. My attempts to recreate defn also fail, and I'm not sure why (I took example from a tutorial):

(defmacro defn2 [name param & body] 
  `(def ~name (fn ~param ~@body)))

(defn2 add [a b] (+ a b)) ;;I get "Use of undeclared Var app.core/defn2"

Can someone please explain, how exactly does Clojure turn data structures, especially symbols, into code? And what am I missing about the macro example?

Update Apparently, macro does not work because my project is actually in Clojurescript (in Clojure it does work). I did not think it matters, but as I progress - I discover more and more things that somehow don't work for me in with Clojurescript.

Update 2 This helps: https://www.clojurescript.org/about/differences

A function is a first-class citizen as other data in Clojure.

To define a vector you use (vector...) or reader has syntaxic sugar [...] , for a list it's (list...) or '(...) the quote not to evaluate the list as a function call, for a set (set...) or #{...} .

So the factory function for a function is fn (in fact fn* , that comes from Java core of Clojure, fn is a series of macros to manage to destructure and all).

(fn args body )

is a function call that returns a function, where args is a vector of argument(s) event. empty and body is a series of Clojure expressions to be evaluated with args bind to the environment. If nothing is to be evaluated it returns nil. There is also a syntactic sugar #(...) with %x as argument x and % as argument 1.

(fn...) return a value that is a function. So

(def my-super-function (fn [ab c d] (println "coucou") (+ ab c d)))

binds the symbol my-super-function with the anonymous function returned by (fn [ab c d] (println "coucou") (+ ab c d)) .

(def my_vector [1 2 3])

binds the symbol my_vector with the vector [1 2 3]

List of learning resources: https://github.com/io-tupelo/clj-template#documentation

As @jas said, your defn2 macro looks fine.

The main point is that macros are an advanced feature that one almost never needs. A macro is equivalent to a compiler extension, and that is almost never the best solution to a problem. Also keep in mind that functions can do some things macros can't.

Another point: the syntax-quote (aka backquote) ` is very different from a single quote '. In your example you want the single quote for ['a 'b] . Even better would be to quote the entire vector form '[ab] .

As to your primary question, it is poorly explained how source-file text is converted into code. This is a 2-step process. The Clojure Reader consumes text string data (from a file or a literal string) and produces data structures like lists, vectors, strings, numbers, symbols. The Clojure compiler takes these data structures as input and produces java byte code that can be executed.

It is confusing because, when printed, one can't tell the difference between the text representation of a vector [1 2 3] and the text string that is input to the reader [1 2 3] . Ideally it would be color-coded or something. This problem doesn't exist in Java, etc since they don't have macros and hence there is no confusion between the source code (text) and the data structures used by a macro (not text).

For a more detailed answer on creating macros in Clojure, please see this answer .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM