What’s the name of my space?

The next thing we need to look at are symbols, keywords and namespaces. Symbols and keywords are used to name things, and the Clojure versions are a bit different from how they work in Common Lisp. In Common Lisp, there is a notion of a package, which is a lot like a Clojure namespace, and there are symbols which have a name and are maybe affiliated with a package. The Clojure version of a symbol has a name and may have a namespace, but the namespace may not exist even if it is present in the symbol.

We can make symbols using the symbol function
user=> (symbol "foo" "bar")
foo/bar
user=> (symbol "bar")
bar

And use the namespace and name functions to deconstruct them
user=> (namespace (symbol "foo" "bar"))
"foo"
user=> (name (symbol "foo" "bar"))
"bar"
user=> (namespace (symbol "bar"))
nil
user=> (name (symbol "bar"))
"bar"

Rather than using the function symbol to make the symbol, the reader automatically makes the symbol when it reads it.
user=> (name ‘bar)
"bar"
user=> (namespace ‘bar)
nil
user=> (name ‘foo/bar)
"bar"
user=> (namespace ‘foo/bar)
"foo"

In Common Lisp, Keywords are symbol with a particular home package – the package that owns the symbol is the package named Keyword. In Clojure, a Keyword is an instance of a class which contains a symbol.
user=> (keyword "foo" "bar")
:foo/bar
user=> (namespace :foo/bar)
"foo"
user=> (name :foo/bat)
"bat"
user=> (keyword "bar")
:bar
user=> (namespace :bar)
nil
user=> (name :bar)
"bar"

Keywords are useful because they evaluate to themselves, whereas symbols are evaluated by looking them up and evaluating the result of this lookup.
user=> :boo
:boo
user=> boo
java.lang.Exception: Unable to resolve symbol: boo in this context (NO_SOURCE_FILE:0)

Just before we look at any more code, remember than the String class in java has an intern method that converts a string to a canonical instance of the same string.
user=> (. (new String "a") (intern))
"a"
user=> (new String "a")
"a"
user=> (identical? *1 *2)
false
user=> (new String "a")
"a"
user=> (new String "a")
"a"
user=> (identical? (new String "a") "a")
false
user=> (. (new String "a") (intern))
"a"
user=> (. (new String "a") (intern))
"a"
user=> (identical? *1 *2)
true

The Symbol class always interns the strings used for the namespace and the name to make later comparison identity comparison instead of equality comparison.
static public Symbol intern(String ns, String name){
    return new Symbol(ns == null ? null : ns.intern(), name.intern());
}

static public Symbol intern(String nsname){
    int i = nsname.lastIndexOf(‘/’);
    if(i == -1)
        return new Symbol(null, nsname.intern());
    else
        return new Symbol(nsname.substring(0, i).intern(), nsname.substring(i + 1).intern());
}

Symbols have metadata.
user=> (def x ‘a)
#’user/x
user=> x
a
user=> (meta x)
nil
user=> (with-meta ‘a {:a 2})
a
user=> (def y *1)
#’user/y
user=> (meta y)
{:a 2}

And compare equal if their name and namespace components are the same.
user=> (= ‘a ‘a)
true
user=> (identical? ‘a ‘a)
false
In this example, the reader is making a new symbol every time it reads one of the “a” characters.

When the Clojure reader reads something from a file, perhaps prior to compiling it, the forms are read as lists of symbols.
user=> (def form ‘(defn foo [x y] (+ x y)))
#’user/form
user=> (first form)
defn
user=> (namespace (first form))
nil
user=> (name (first form))
"defn"
user=> (namespace (second form))
nil
user=> (name (second form))
"foo"

Namespaces are the next thing we need to understand in order to see how the system evaluates the form contained in the Var named form. The first symbol, “defn” will be resolved in the current namespace to see if it denotes a Var.
user=> *ns*
#<Namespace user>
user=> (first form)
defn
user=> (resolve (first form))
#’clojure.core/defn
user=> (ns-resolve *ns* (first form))
#’clojure.core/defn

The Java class for Namespace contains the following fields.
  public class Namespace extends AReference{
    final public Symbol name;
    final AtomicReference<IPersistentMap> mappings = new AtomicReference<IPersistentMap>();
    final AtomicReference<IPersistentMap> aliases = new AtomicReference<IPersistentMap>();
    final static ConcurrentHashMap<Symbol, Namespace> namespaces = new ConcurrentHashMap<Symbol, Namespace>();
The namespace is named by a Symbol and there is a global map of symbols to namespaces. Each namespace has a group of mappings and some aliases.

When a namespace is constructed in the java code, the constructor deals with metadata, sets the name of the namespace, sets the aliases to an empty map and initializes the mappings to a default set.
Namespace(Symbol name){
    super(name.meta());
    this.name = name;
    mappings.set(RT.DEFAULT_IMPORTS);
    aliases.set(RT.map());
}

The mappings of a namespace. accessed by the Clojure function ns-map, maps from symbols to Var or Class instances.
user=> (ns-map (find-ns ‘user))
{sorted-map #’clojure.core/sorted-map, read-line #’clojure.core/read-line, re-pattern #’clojure.core/re-pattern, keyword? #’clojure.core/keyword?, val #’clojure
.core/val, ProcessBuilder java.lang.ProcessBuilder, Enum java.lang.Enum, SuppressWarnings java.lang.SuppressWarnings, *compile-path* #’clojure.core/*compile-path*,par #’user/par, max-key #’clojure.core/max-key, list* #’clojure.core/list*, ns-aliases #’clojure.core/ns-aliases, the-ns #’clojure.core/the-ns, == #’clojure.core/==, longs #’clojure.core/longs, special-form-anchor #’clojure.core/special-form-anchor, Throwable java.lang.Throwable, InterruptedException java.lang.InterruptedException, instance? #’clojure.core/instance?, syntax-symbol-anchor #’clojure.core/syntax-symbol-anchor, Thread$UncaughtExceptionHandler java.lang.Thread$UncaughtExceptionHandler, RuntimeException java.lang.RuntimeException, …..}

RT.DEFAULT_IMPORTS is a mappings consisting of a large number of predefined Java classes.
final static IPersistentMap DEFAULT_IMPORTS = map(
  Symbol.create("Boolean"), Boolean.class,
  Symbol.create("Byte"), Byte.class,
  Symbol.create("Character"), Character.class,
  Symbol.create("Class"), Class.class,
  Symbol.create("ClassLoader"), ClassLoader.class,
  Symbol.create("Compiler"), Compiler.class,
  Symbol.create("Double"), Double.class,
  Symbol.create("Enum"), Enum.class,
  Symbol.create("Float"), Float.class,
  ………… }

Hence, all newly created namespaces start with these mappings.
user=> (find-ns ‘myNamespace)
nil
user=> (create-ns ‘myNamespace)
#<Namespace myNamespace>
user=> (find-ns ‘myNamespace)
#<Namespace myNamespace>
user=> (ns-resolve (find-ns ‘myNamespace) ‘Boolean)
java.lang.Boolean
user=> (ns-resolve (find-ns ‘myNamespace) ‘boolean)
nil

The Namespace class supports methods all, remove, find, findOrCreate for accessing a sequence of all known namespaces and for adding and removing from this set.
user=> (all-ns)
(#<Namespace clojure.set> #<Namespace user> #<Namespace clojure.main> #<Namespace clojure.core> #<Namespace clojure.zip> #<Namespace clojure.xml>)
user=> (create-ns ‘foo)
#<Namespace foo>
user=> (all-ns)
(#<Namespace clojure.set> #<Namespace user> #<Namespace clojure.main> #<Namespace clojure.core> #<Namespace clojure.zip> #<Namespace foo> #<Namespace clojure.xml>)
user=> (remove-ns ‘foo)
#<Namespace foo>
user=> (all-ns)
(#<Namespace clojure.set> #<Namespace user> #<Namespace clojure.main> #<Namespace clojure.core> #<Namespace clojure.zip> #<Namespace clojure.xml>)
user=> (find-ns ‘clojure.core)
#<Namespace clojure.core>
user=> (find-ns ‘foo)
nil

Symbols can be interned into a namespace in order to make a named Var binding.
user=> (create-ns ‘foo)
#<Namespace foo>
user=> (intern (find-ns ‘foo) ‘bar)
#’foo/bar

This uses one of the intern methods on Var
public static Var intern(Symbol nsName, Symbol sym){
    Namespace ns = Namespace.findOrCreate(nsName);
    return intern(ns, sym);
}
which calls back into the intern method on the Namespace
public static Var intern(Namespace ns, Symbol sym){
    return ns.intern(sym);
}
which does the actual work
public Var intern(Symbol sym){
    if(sym.ns != null)
        {
        throw new IllegalArgumentException("Can’t intern namespace-qualified symbol");
        }
    IPersistentMap map = getMappings();
    Object o;
    Var v = null;
    while((o = map.valAt(sym)) == null)
        {
        if(v == null)
            v = new Var(this, sym);
        IPersistentMap newMap = map.assoc(sym, v);
        mappings.compareAndSet(map, newMap);
        map = getMappings();
        }
    if(o instanceof Var && ((Var) o).ns == this)
        return (Var) o;
    throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
}

Lookup of a symbol in a namespace is via the resolve function (which will use the current namespace, that in the *ns* Var)
user=> (resolve ‘foo/bar)
#’foo/bar

The functions implementing this are in core.clj
(defn resolve
  "same as (ns-resolve *ns* symbol)"
  [sym] (ns-resolve *ns* sym))

(defn ns-resolve
  "Returns the var or Class to which a symbol will be resolved in the
  namespace, else nil.  Note that if the symbol is fully qualified,
  the var/Class to which it resolves need not be present in the
  namespace."
  [ns sym]
  (clojure.lang.Compiler/maybeResolveIn (the-ns ns) sym))

Symbols from one namespace can be made visible in another.
user=> (ns-resolve ‘foo ‘mySym)
nil
user=> (create-ns ‘other)
#<Namespace other>
user=> (intern ‘other ‘mySym)
#’other/mySym
user=> (binding [*ns* (find-ns ‘foo)] (refer ‘other))
nil
user=> (ns-resolve ‘foo ‘mySym)
#’other/mySym
user=> (ns-resolve ‘foo ‘other/mySym)
#’other/mySym

Internally this calls the refer method of the Namespace object
public Var refer(Symbol sym, Var var){
    return (Var) reference(sym, var);
}
which calls the reference method
Object reference(Symbol sym, Object val){
    if(sym.ns != null)
        {
        throw new IllegalArgumentException("Can’t intern namespace-qualified symbol");
        }
    IPersistentMap map = getMappings();
    Object o;
    while((o = map.valAt(sym)) == null)
        {
        IPersistentMap newMap = map.assoc(sym, val);
        mappings.compareAndSet(map, newMap);
        map = getMappings();
        }
    if(o == val)
        return o;
    throw new IllegalStateException(sym + " already refers to: " + o + " in namespace: " + name);
}

Import can be used to map java classes into a namespace. ns-unmap can be used to remove any existing mapping.
user=> Thread
java.lang.Thread
user=> (ns-unmap *ns* ‘Thread)
nil
user=> Thread
java.lang.Exception: Unable to resolve symbol: Thread in this context (NO_SOURCE
_FILE:0)
user=> (import ‘(java.lang Thread))
nil
user=> Thread
java.lang.Thread

One can also set up aliases for a given namespace to allow it to be accessed via another symbol when that symbol is used as the namespace part of the lookup symbol. There are also function ns-publics and ns-imports for getting subsets of the main namespace map.

Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s