Ha feltelepítünk egy Clojure REPL-t és megnyitjuk, a megjelenő felületen az első, amit látunk, ez lesz:
user=>
...ahol a "user" szimbólum jelzi az aktuális névtér nevét. A névtér a clojure.lang.Namespace osztály egy objektuma, és lényegében egy leképezésként szolgál: szimbólumokat képez le Var-okra és osztályokra. A programunk futása során több névtér objektum is lehet a memóriában, az aktuális névtér nevét az *ns*-sel kérdezhetjük le, az összes névtér objektum listáját az (all-ns) hívással kapjuk meg. Számos módszer létezik új névterek létrehozására, ilyenek az in-ns, a create-ns és az ns makró. Ezek közül a create-ns a legritkábban használt, legalacsonyabb szintű módszer:
user=> (def v (create-ns 'nevter)) ; a v var-ba belerakunk egy névtér objektumot #'user/v user=> (class v) clojure.lang.Namespace
A create-ns egy teljesen üres névteret hoz létre, ami azt jelenti, hogy a fenti 'nevter nevű névterünkből nem hivatkozhatunk semmilyen osztályra a clojure.core és a java.lang csomagokból. Ezzel szemben az in-ns a névterek közötti navigációra illetve névterek létrehozására is használható - az in-ns tehát a következőket teszi: "névteret vált" (a megadott nevű névtérre állítja az aktuális névteret jelző *ns* értéket), illetve ha addig még nem volt olyan névtér, amire váltani akarunk, akkor létrehoz egyet. Az in-ns-sel létrehozott névtér hivatkozik a java.lang csomagra, de a clojure.core-ra nem.
user=> (in-ns 'nevter2) #<Namespace nevter2> nevter2=> (reduce + '(1 2 3 4)) ; az in-ns-sel létrehozott névtér nem hivatkozik a clojure.core-ra java.lang.Exception: Unable to resolve symbol: reduce in this context (NO_SOURCE_FILE:11)
A REPL-ben való kísérletezgéshez inkább az in-ns használatát ajánlják, a forráskódfájlokban való névtérdefiníciókhoz viszont a később bemutatásra kerülő ns makrót.
Felmerül a kérdés, hogy amennyiben az előbb látott in-ns-sel hozunk létre egy névteret, hogyan hivatkozhatunk mégis a clojure.core csomagban lévő névterekre? Erre ad választ a require:
user=> (require 'clojure.string '[clojure.test :as teszt]) ; lehetne ['clojure.test :as 'teszt] is... nil user=> (require '(clojure [string :as string] test)) ; előzővel ekvivalens: mivel mindkét névtér a clojure-höz tartozik, ezért azt kiemeljük nil user=> (teszt/is (= 4 4)) ; az is függvény a clojure.test névtérben helyezkedik el true user=> (clojure.string/replace "alma" "a" "afa") ; a replace pedig a clojure.string-ben "afalmafa"
Látható, hogy a require-rel betöltött névterek függvényeit csak a teljesen minősített nevükkel, a névtér/függvény vagy alias/függvény formátumban lehet elérni a require hívás után. De mi történik a háttérben? A require-nek megadható egy :verbose flag is, ami pontosan mutatja, valójában mi is zajlik a háttérben. Frissen megnyitott REPL-be írjuk be:
user=> (require 'clojure.string '[clojure.test :as teszt] :verbose) (clojure.core/load "/clojure/test") (clojure.core/load "/clojure/template") (clojure.core/load "/clojure/walk") (clojure.core/in-ns 'clojure.template) (clojure.core/alias 'walk 'clojure.walk) (clojure.core/in-ns 'clojure.test) (clojure.core/alias 'temp 'clojure.template) (clojure.core/load "/clojure/stacktrace") (clojure.core/in-ns 'clojure.test) (clojure.core/alias 'stack 'clojure.stacktrace) (clojure.core/in-ns 'user) (clojure.core/alias 'teszt 'clojure.test) nil user=> (require 'clojure.string '[clojure.test :as teszt] :verbose) (clojure.core/in-ns 'user) (clojure.core/alias 'teszt 'clojure.test) nil
Két dolgot vehetünk észre: egyrészt, ha egy require-rel kétszer töltünk be egy névteret, azt valójában csak egyszer tölti be a rendszer (load). A második észrevétel az, hogy ha egy olyan névteret töltünk be a require-rel, ami maga is tartalmaz egy require hívást, akkor egyúttal azokat a névtereket is betölti (fenti példában így töltődött be a clojure.template, clojure.stacktrace, stb.) Persze rendelkezhetünk úgy is, hogy a rendszer az adott require hívásban újratöltse a paraméterként megadott névtereket, ebben az esetben a :reload flag-et kell kiírnunk, a :verbose flag-hez hasonlóan.
Említettük, hogy a require hívás után a névtér/függvénynév formátumban lehet hívni a betöltött névtér függvényeit. Ez lehet, hogy kényelmetlen, még alias-ok használatával is, ezért bevezették a use-t:
user=> (use '[clojure.string :as str :only [join split]]) ; az :as str rész elhagyható (nem kötelező névtér-alias-t adni) nil user=> (split "a,b,c,d" #",") ["a" "b" "c" "d"] user=> (str/split "a,b,c,d" #",") ; de ha adtunk a névtérnek alias-t, így is hívhatjuk ["a" "b" "c" "d"]
A fenti példában az :only kapcsoló jelentősége az, hogy csak a join és a split függvényekre hivatkozhatunk kizárólag a függvények nevét használva. Az :only kapcsoló használata nélkül a kód gyakran átláthatatlanná válhat, mert a különböző névterekben lévő függvények elfedhetik egymást, ezért a use hívásoknál nagyon javasolt az :only kapcsoló használata. Egy másik lehetőség az :exclude kapcsoló, amivel az :exclude-ban megadott függvények kivételével a névtérben szereplő minden függvényt használhatunk teljesen minősített név nélkül, azonban hosszú távon ez a módszer sem garantálhatja a névütközés-mentességet.
Mit tehetünk akkor, ha két különböző névtérben lévő függvénynek ugyanaz a neve? A use megengedi a függvények "átnevezését":
user=> (use '[clojure.string :rename {replace str-replace, reverse str-reverse})
A require és a use használata után az ns makró használata már természetes lesz, hiszen ez a makró egyesíti a require és a use makrók használatát. Lássunk egy összetett példát:
(ns nevter "egy bonyolult nevter" ; dokumentációs sztring (:refer-clojure :exclude [defstruct]) ; a :refer-clojure elhagyható, ekkor a (refer 'clojure) szerinti viselkedés az alapértelmezett (:use (clojure set xml)) (:use [clojure.test :as teszt :only (are is)] :reload) (:require (clojure [zip :as z]) [clojure.inspector :as insp] ; a require libspec-eket az ns makrón belül nem kell idézőjelezni (clojure template walk) :verbose) (:import (java.util Date GregorianCalendar) ; ezeket a Java osztályokat a teljesen minősített nevük nélkül lehet ezután használni (java.io File)))