Zaz
Zaz

Reputation: 48729

`def` vs `declare` for forward declaration

Clojure, has a declare macro that allows you to forward-declare functions or variables. It seems to function exactly as def: Both (declare x) and (def x) create #<Unbound Unbound: #'user/x>

When should (declare x) be used instead of (def x)?

Upvotes: 23

Views: 1713

Answers (3)

jackotonye
jackotonye

Reputation: 3853

def always applies to the root binding, even if the var is thread-bound at the point where def is called.

def yields the var itself (not its value). Throws an exception if symbol is already in the namespace and not mapped to an interned var.

Declare defs the supplied var names with no bindings, useful for making forward declarations.

Upvotes: 1

munk
munk

Reputation: 12983

The documentation gives the answer:

=> (doc declare)
-------------------------
clojure.core/declare
([& names])
Macro
  defs the supplied var names with no bindings, useful for making forward declarations.

Looking at the implementation, it's clear that declare is defined in terms of def and provides a little bit of syntax sugar. So functionally, they're pretty much the same.

The advantage of declare is to show intent for a later reader. (declare x y z) means I intended to make a forward declaration of those symbols, because the macro is useful for making forward declarations.

(def x) (def y) (def z) means I'm interning these symbols, but you don't know if I meant to give them definitions and forgot to or whether I'm making forward declarations, or maybe something else subtle.

So, (declare x) should be preferred over (def x) when you're making a forward declaration, to show mercy on future readers of your code.

Upvotes: 6

Zaz
Zaz

Reputation: 48729

Both declare and def do create an unbound var, but there are 3 advantages to using declare:

  1. You can create multiple vars in one statement, e.g. (declare x y z)
  2. The vars are tagged with the additional metadata {:declared true}
  3. Using the word declare is arguably more clear and idiomatic

(source declare):

(defmacro declare
  "defs the supplied var names with no bindings, useful for making forward declarations."
  {:added "1.0"}
  [& names] `(do ~@(map #(list 'def (vary-meta % assoc :declared true)) names)))

Upvotes: 26

Related Questions