Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between Symbols and Vars in Clojure

Tags:

clojure

I'm always a bit confused about Symbols and Vars in Clojure. For example, is it safe to say that + is a symbol which is used to denote a var, and this var points to a value which is a function that can add numbers?

So what happens, step by step when I just enter "+" in a REPL?

  1. The symbol gets qualified to a namespace, in this case clojure.core
  2. Then in some symbol table there is the information that + refers to a var
  3. When this var is evaluated, the result is a function-value?
like image 698
Michiel Borkent Avatar asked Feb 02 '12 13:02

Michiel Borkent


People also ask

What are symbols in Clojure?

In Common Lisp, a "symbol" is a location in memory, a place where data can be stored. The "value" of a symbol is the data stored at that location in memory. In Clojure, a "symbol" is just a name. It has no value.

What is a VAR in Clojure?

Thus, the value of a Var is the value of its per-thread binding, or, if it is not bound in the thread requesting the value, the value of the root binding, if any. The special form def creates (and interns) a Var.

What is binding in Clojure?

;; binding creates a dynamically scoped binding for some Var. ;; Dynamic binding means that the code inside your binding form and any code ;; which that code calls (even if not in the local lexical scope) will see the new binding.

What is let in Clojure?

Clojure let is used to define new variables in a local scope. These local variables give names to values. In Clojure, they cannot be re-assigned, so we call them immutable.


2 Answers

There's a symbol + that you can talk about by quoting it:

user=> '+ + user=> (class '+) clojure.lang.Symbol user=> (resolve '+) #'clojure.core/+ 

So it resolves to #'+, which is a Var:

user=> (class #'+) clojure.lang.Var 

The Var references the function object:

user=> (deref #'+) #<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf> user=> @#'+ #<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf> 

(The @ sign is just shorthand for deref.) Of course the usual way to get to the function is to not quote the symbol:

user=> + #<core$_PLUS_ clojure.core$_PLUS_@55a7b0bf> 

Note that lexical bindings are a different mechanism, and they can shadow Vars, but you can bypass them by referring to the Var explicitly:

user=> (let [+ -] [(+ 1 2) (@#'+ 1 2)]) [-1 3] 

In that last example the deref can even be left out:

user=> (let [+ -] [(+ 1 2) (#'+ 1 2)]) [-1 3] 

This is because Var implements IFn (the interface for Clojure functions) by calling deref on itself, casting the result to IFn and delegating the function call to that.

The visibility mechanism used when you define private functions with defn- is based on metadata on the symbol. You can bypass it by referring directly to the Var, as above:

user=> (ns foo) nil foo=> (defn- private-function [] :secret) #'foo/private-function foo=> (in-ns 'user) #<Namespace user> user=> (foo/private-function) java.lang.IllegalStateException: var: #'foo/private-function is not public (NO_SOURCE_FILE:36) user=> (#'foo/private-function) :secret 
like image 160
Jouni K. Seppänen Avatar answered Sep 20 '22 23:09

Jouni K. Seppänen


See the documentation for namespaces:

Namespaces are mappings from simple (unqualified) symbols to Vars and/or Classes. Vars can be interned in a namespace, using def or any of its variants, in which case they have a simple symbol for a name and a reference to their containing namespace, and the namespace maps that symbol to the same var. A namespace can also contain mappings from symbols to vars interned in other namespaces by using refer or use, or from symbols to Class objects by using import.

So basically your steps 1 and 2 are unified: the namespaces are the symbol tables.

And regarding step 3: I like the definition for variables that they're combinations of names of values. The symbol is the variable's name, and evaluating it will result in its value.

like image 26
Rörd Avatar answered Sep 21 '22 23:09

Rörd