Clojure Tutorial, Teil 2: Namespaces
Von Carsten
Dies ist der zweite Artikel in einer kleinen Serie, die meine ersten Schritte in der neuen funktionalen Programmiersprache Clojure dokumentieren soll. Die Artikel werden in unregelmäßigen Abständen hier publiziert.
Im letzten Artikel habe ich in dem erweiterten Hallo Welt eine Var
definiert, in der ich die auszugebende Nachricht "Hallo Welt"
abgelegt hatte. Man könnte jetzt glauben, eine Var
ist gleichbedeutend mit einer globalen Variable, aber das ist nicht so.
Namespaces
In Clojure sind alle Variablen und Funktionen “gescoped”, d.h. mit einem Gültigkeitsbereich belegt. Startet man die REPL, wird automatisch der Namespace “user” angelegt. Man kann den Namespace einfach mit der special form ns wechseln:
$ java -cp clojure.jar clojure.main
Clojure 1.3.0-alpha3-SNAPSHOT
user=> (ns mynamespace)
nil
mynamespace=>
Mit der Funktion (ns mynamespace) erstelle ich einen neuen Namespace und wechsel direkt dort hinein. Anschliessend kann ich dort Vars und Funktionen erstellen, und zwar ohne explizit den Namespace angeben zu müssen. Allerdings kann ich sie ausserhalb meines Namespaces nur voll qualifiziert referenzieren, d.h. ich muss immer den Namespace mit angeben:
mynamespace=> (def msg "Hallo Welt")
#'mynamespace/msg
mynamespace=> (println msg)
Hallo Welt
nil
mynamespace=> (ns user)
nil
user=> (println msg)
CompilerException java.lang.Exception: Unable to resolve symbol: msg in this context, compiling:(NO_SOURCE_PATH:5)
user=> (println mynamespace/msg)
Hallo Welt
nil
user=>
Ich habe die Var mynamespace/msg angelegt und anschliessend in einen anderen Namespace gewechselt. Dort habe ich versucht, die Var msg auszugeben, die im Namespace user gar nicht existiert und der Compiler hat das mit einer Fehlermeldung quittiert. Anschliessend habe ich den vollständigen Namen mynamespace/msg verwendet und konnte die Var erfolgreich ausgeben.
Organisation in Dateien
Um die Organisation der Quelltexte in Dateien zu vereinfachen, gibt es eine kleine Regel:
Jede Datei hat ihren eigenen Namespace, die erste Form in einer Datei ist die special form ns.
Wenn man allerdings in einer Datei sehr oft Funktionen eines anderen Namespace verwendet, kann man den Schreibaufwand etwas reduzieren, in dem man alle Funktionen des anderen Namespace in den eigenen importiert. Dazu verwendet man die :use Direktive der special form ns:
user=> (ns mynamespace (:use clojure.xml))
nil
mynamespace=> parse
#<xml$parse clojure.xml$parse@55a7b0bf>
mynamespace=>
Die seltsame Ausgabe ist Clojures Repräsentation der Funktion parse, das kann jetzt erstmal ignoriert werden. Der springende Punkt ist die Tatsache, dass ich die Funktion clojure/xml/parse ohne Angabe des voll qualifizierten Namen aufrufen kann. Damit werden die eigenen Quelltexte wieder etwas kürzer und hoffentlich lesbarer.