클로저의 이름공간(namespace)은 자바의 패키지(package)에 해당하는 개념으로, 기본적으로 라이브러리들 사이의 이름 충돌(name clash)을 방지해 주고, 다른 클로저 또는 자바 라이브러리들의 사용을 원활하게 해주는 역할을 수행한다.

클로저에서 REPL을 실행하면 다음의 예에서 볼 수 있듯이 기본적으로 user 이름공간 안에서 실행된다. def 문을 평가한 결과로 var가 반환되었는데 이때 이름공간 user가 앞에 붙어 있는 것에 주목할 필요가 있다. 이 var가 소속된 이름공간이 user임을 명시적으로 표시하고 있다.

user> (def a 10)
#'user/a

실제 이름공간 관련 작업을 할 때는 주로 ns 매크로를 사용하지만 이에 대한 설명은 나중으로 미루고, require, use, import 같은 함수들을 먼저 다루겠다. 그 이유는 ns가 배후에서 이 함수들을 이용하는 매크로에 불과하기 때문이다.

1. require

먼저 예제에서 사용할 라이브러리 clj-timeproject.clj 파일의 :dependencies 부분에 다음과 같이 추가한다.

[clj-time "0.8.0"]

이 상태에서 다음과 같이 실행하면 클래스를 찾을 수 없다는 예외가 발생하는데, 그 이유는 clj-time.core 모듈이 메모리에 아직 로드되지 않았기 때문이다.

(clj-time.core/date-time 2015 9 10 3 25 28 456)
;>> ClassNotFoundException clj-time.core  java.net.URLClassLoader.findClass

require 함수는 인수로 주어진 모듈이 이미 메모리에 로드되어 있지 않은 경우에만 해당 모듈을 로드하고, 이미 로드되어 있으면 다시 로드를 시도하지 않는다.

Note
모듈 이름 앞에 인용(') 기호가 붙어야 한다는 점에 주의한다. 다시 말해, 이 함수의 인수는 반드시 심볼형이어야 한다.
(require 'clj-time.core)

(clj-time.core/date-time 2015 9 10 3 25 28 456)
;=> #object[org.joda.time.DateTime 0x56c4a13 "2015-09-10T03:25:28.456Z"]

require 함수는 모듈 이름을 여러 개 나열할 수도 있다.

(require 'clj-time.core 'clojure.string)

1.1. 벡터 형태의 인수

제대로 실행되었지만 함수를 실행할 때마다 이름공간인 clj-time.core를 함수명 앞에 매번 붙여 주어야 한다면 상당히 짜증날 것이다. 이름공간을 벡터 안에 넣은 후 :as 키워드 뒤에 별칭(alias)을 부여해 대신 사용할 수 있다. 이 경우에도 벡터 앞에 인용(') 기호 붙이는 것을 잊지 않도록 한다.

(require '[clj-time.core :as time])

;;위의 코드는 아래를 실행한 것과 같다.
;;(require 'clj-time.core)
;;(alias 'time 'clj-time.core)

(time/date-time 2015 10 10 3 25 28 456)
;=> #object[org.joda.time.DateTime 0x1c27e3aa "2015-10-10T03:25:28.456Z"]

require 함수의 벡터 형태의 인수에는 :refer 키워드도 올 수 있는데, 이 키워드 뒤에 오는 벡터에 나열된 함수명들에 한해 이름공간을 붙이지 않고 직접 호출하는 것이 허용된다.

(require '[clojure.string :as str :refer [upper-case capitalize]])

;;위의 코드는 아래를 실행한 것과 같다.
;;(require 'clojure.string)
;;(alias 'str 'clojure.string)
;;(refer 'clojure.string :only '[upper-case capitalize])

(upper-case "korean")    ;=> "KOREAN"
(capitalize "korean")    ;=> "Korean"
(str/lower-case "USA")   ;=> "usa"

(lower-case "USA")
;>> CompilerException java.lang.RuntimeException:
;     Unable to resolve symbol: lower-case in this context,

:refer 키워드 뒤에는 :all 키워드도 올 수 있는데, 해당 모듈의 모든 함수를 이름공간 없이 호출할 수 있다. 이 경우는 뒤에 설명할 use 함수를 사용한 것과 같은 효과를 내는데, require 함수 내에서 :refer :all을 붙여 사용하는 것이 더 권장된다. (하지만 이름공간을 붙이지 않고 사용하는 것 자체는 권장할 만한 일은 아니다. 실제로 이러한 이유로 인해 클로저스크립트에서는 :refer :all이 허용되지 않는다. 다만 클로저에서는 하위 호환성을 위해서 남아있을 뿐이다)

(require '[clojure.string :refer :all])

(lower-case "USA")   ;=> "usa"

1.2. 리스트 형태의 인수

require 함수는 리스트 형태의 인수도 받을 수 있다. 이 경우 리스트의 첫 번째 요소가 공통 인수 역할을 한다. 예를 들어 clj-time 라이브러리는 core 모듈 이외에도 format, coerce 모듈도 함께 제공하는데, 이 세 모듈을 모두 require하고자 할 때 다음의 두 표현은 같은 결과를 낳는다.

(require 'clj-time.core 'clj-time.format 'clj-time.coerce)

(require '(clj-time core format coerce))

앞에서 배운 벡터 형태의 인수와 결합해 사용할 수도 있다. 다음의 두 표현도 같은 결과를 낳는다.

(require 'clojure.zip
         '[clojure.set :as set])

(require '(clojure zip [set :as set]))

1.3. flags

require 함수는 :reload, :reload-all, :verbose 3 종류의 플래그(flag)를 인수로 취할 수 있다. 이 플래그들은 라이브러리를 개발할 때 매우 유용하다.

앞에서 require 함수는 이미 로드된 모듈은 다시 로드하지 않는다고 했다. 하지만 라이브러리 개발 도중에는 이미 로드된 모듈도 다시 강제로 require할 필요가 생긴다. 이름공간 example.aexample.b를, example.b가 다시 example.c를 require하는 경우를 예로 들어 보자.

            require               require
example.a  -------->  example.b  -------->  example.c

다음과 같이 :reload 플래그를 주면, example.a 모듈이 평가될 때 example.b 모듈이 이미 로드되어 있어도 example.b 모듈이 다시 강제로 로드된다.

:reload 플래그
;;example/a.clj
(ns example.a
  (:require [example.b :as b] :reload))   ;(1)

;;example/b.clj
(ns example.b
  (:require [example.c :as c]))

;;example/c.clj
(ns example.c)

다음 같이 :reload-all 플래그를 주면, example.a 모듈이 평가될 때 example.b 모듈뿐만 아니라, example.b 모듈이 다시 require하고 있는 example.c 모듈까지 줄줄이 다시 강제로 로드된다.

:reload-all 플래그
;;example/a.clj
(ns example.a
  (:require [example.b :as b] :reload-all))   ;(2)

;;example/b.clj
(ns example.b
  (:require [example.c :as c]))

;;example/c.clj
(ns example.c)

:verbose 플래그를 주면, 이름공간이 require될 때 일어나는 과정과 관련되는 정보를 상세히 출력해 준다. 따라서 이름공간과 관련된 작업이 실제 어떻게 일어아는지 직접 확인하고 싶을 떄 사용하면 유용하다.

:verbose 플래그
(require '[clojure.string :as str] :verbose)   ;(3)
;>> (clojure.core/in-ns 'user)
;   (clojure.core/alias 'str 'clojure.string)
;=> nil

2. refer

refer 함수는 require 함수의 인수에 :refer 옵션이 있거나 use 함수가 호출될 때 이 함수들의 내부에서 이용되는 함수로, 개발자가 requireuse같은 일을 하는 함수를 직접 개발할 일이 없는 한 호출할 일은 거의 없는 함수이지만, 그 작동 원리는 이해할 필요가 있다.

클로저는 이름공간마다 다음과 같은 형태(실제로는 map 자료형의 키/값 쌍)로 심볼 테이블의 항목들을 유지한다.

symbol --> var

예를 들어 user 이름공간에서 다음과 같이 upper-case 함수를 정의(자세한 구현은 생략)하고 실행해 보면 예상한 대로 결과가 나온다.

user> (defn upper-case [s]
        (str "My upper-case function: arg = " s))
#'user/upper-case

user> (upper-case "hello")
"My upper-case function: arg = hello"

이때 user 이름공간의 심볼 테이블에는 다음의 항목이 새로 추가된다.

user 심볼 테이블
upper-case --> #'user/upper-case

이제 user 이름공간에서 clojure.string 이름공간을 refer한 후 upper-case 함수를 호출해 보자.

Note
refer 함수는 require 함수와는 달리, 한 개의 이름공간만을 인수로 지정할 수 있다.
user> (refer 'clojure.string)

user> (upper-case "hello")
"HELLO"

방금 전에 user 이름공간에서 정의한 upper-case 함수는 사라지고, clojure.string 이름 공간의 upper-case 함수가 실행되었다. 그 이유는 user 이름공간의 심볼 테이블의 upper-case 항목이 다음과 같이 바뀌었기 때문이다.

user 심볼 테이블
upper-case --> #'clojure.string/upper-case

다시 말해 refer 함수를 호출하면, 인수로 지정된 이름공간의 모든 public var와 관련된 심볼 테이블 항목들이 현재의 이름공간의 심볼 테이블에 복사하는 방식으로 추가되면서, 이미 정의되어 있는 항목들은 덮어 쓰게 된다. 따라서 위에서 본 것 처럼, 예기치 않은 위험한 상황이 초래될 수 있으므로 특별한 상황이 아니면, 다음과 같이 필터를 주어 사용하는 것이 좋다.

2.1. filters

refer 함수는 :only, :exclude, :rename 필터를 사용할 수 있다.

:only 키워드 뒤에 사용하고 싶은 심볼들을 나열하면, clojure.string 이름공간에서 그 심볼들만을 현재의 이름공간의 심볼 테이블에 추가한다.

(refer 'clojure.string
       :only '[upper-case])

(upper-case "world")   ;=> "WORLD"

(lower-case "UNESCO")
;>> CompilerException java.lang.RuntimeException:
;     Unable to resolve symbol: lower-case in this context

(clojure.string/lower-case "NASA")   ;=> "nasa"

반대로 :exclude 키워드 뒤에 심볼들을 나열하면, 그 심볼들을 제외한 나머지 모든 심볼들을 현재의 이름공간의 심볼 테이블에 추가한다.

(refer 'clojure.string
       :exclude '[lower-case])

(lower-case "UFO")
;>> CompilerException java.lang.RuntimeException:
;     Unable to resolve symbol: lower-case in this context

(upper-case "love")   ;=> "LOVE"

:rename 키워드 뒤에 맵의 형태로, 사용하고자 하는 심볼들의 이름을 자신이 원하는 이름으로 변경할 수 있다.

(refer 'clojure.string
       :rename '{upper-case upcase})

(upcase "people")   ;=> "PEOPLE"

;;upper-case는 더 이상 사용할 수 없다.
(upper-case "ruby")
;>> CompilerException java.lang.RuntimeException:
;     Unable to resolve symbol: upper-case in this context

;;사용하려면 clojure.string 이름공간을 붙여 주여야 한다.
(clojure.string/upper-case "ruby")   ;=> "RUBY"

3. use

use 함수는 refer 함수를 확장한 것으로 보면 좋다. 그래서 refer 함수에서 사용한 모든 키워드 옵션을 그대로 사용할 수 있다. 다른 점은, require 함수처럼 이름 공간을 여러 개 지정할 수 있고, 벡터 형태리스트 형태의 인수도 require 함수에서처럼 허용된다. 심지어는 require 함수에서 사용한 플래그들도 그대로 사용할 수 있다. 그래서 다음과 같이 use 함수를 requirerefer 함수를 하나로 합쳐 놓은 것으로 흔히들 많이 설명한다.

use = require + refer

다음은 use 함수에 다양한 형태의 인수들을 사용한 예이다.

(use 'clojure.test
     '[clojure.string :rename {capitalize cap reverse rev}
                      :only [capitalize trim]]
     '(clojure.java io shell)
     :reload
     :verbose)
;>> (clojure.core/load "/clojure/test")
;   (clojure.core/in-ns 'user)
;   (clojure.core/refer 'clojure.test)
;   (clojure.core/load "/clojure/string")
;   (clojure.core/in-ns 'user)
;   (clojure.core/refer 'clojure.string :rename '{capitalize cap, reverse rev}
;                                       :only '[capitalize trim])
;   (clojure.core/load "/clojure/java/io")
;   (clojure.core/in-ns 'user)
;   (clojure.core/refer 'clojure.java.io)
;   (clojure.core/load "/clojure/java/shell")
;   (clojure.core/in-ns 'user)
;   (clojure.core/refer 'clojure.java.shell)
;=> nil

use 함수가 refer 함수와 다른 점은 :as 키워드를 붙여 별칭(alias)을 사용할 수 있다는 것이다.

(use '[clojure.string :as str :only [split]])

;;clojure.string 대신 별칭 str을 쓸 수 있다.
(str/replace "foobar" "bar" "baz")   ;=> "foobaz"

;;split 함수의 경우에는 이름공간 없이 사용할 수 있다.
(split "hello world" #" ")   ;=> ["hello" "world"]

사실 다음 두 표현은 같은 일을 한다.

(use '[clojure.string :as str :only [split]])

(require '[clojure.string :as str :refer [split]])

따라서 이런 경우에 굳이 use 함수를 사용할 필요는 없을 것이다. 하지만 다음과 같이 use 함수에서만 제공하는 키워드 옵션을 사용해야 하는데, require 함수에서처럼 별칭을 사용하고 싶을 때에는 use 함수에서 :as 키워드를 사용하는 것이 불가피해진다.

(use '[clojure.string :as str
                      :rename {capitalize cap reverse rev}
                      :only [upper-case]])

(lower-case "ASCII")
;>> CompilerException java.lang.RuntimeException:
;     Unable to resolve symbol: lower-case in this context

(str/lower-case "ASCII")   ;=> "ascii"
(upper-case "physcs")      ;=> "PHYSICS"
(cap "math")               ;=> "Math"

4. import

JRE에 기본적으로 탑재된 자바 클래스들은, 해당 패키지의 모든 경로명을 앞에 붙여주면 import할 필요 없이 직접 호출할 수 있다.

(def date (java.util.Date.))

date   ;=> #inst "2015-09-10T07:49:28.622-00:00"

4.1. 자바 클래스 import 하기

하지만, 클래스명 앞에 패키지 경로명을 매번 일일이 붙여주어야 하므로, 반복해서 사용해야 하는 경우에는 불편하다. 이런 경우에 import 함수를 사용한다.

(import java.util.Date)

(def date (Date.))

date   ;=> #inst "2015-09-10T07:52:54.847-00:00"
Note

import 함수에서 자바 클래스를 개별적으로 나열할 때에는, 인용 기호를 붙이지 않아도 된다. 다음의 두 형태 모두 허용된다.

(import java.util.Date)

(import 'java.util.Date)
Note

java.lang 패키지의 모든 클래스들은 디폴트로 모두 import된다. 예를 들어 java.lang.String 클래스는 String이라는 심볼을 통해 직접 접근이 가능하다. 따라서 이 클래스를 별도로 import해줄 필요는 없다.

(def hello (String. "Hello World!"))

hello   ;=> "Hello World!"

require 함수의 경우처럼 리스트 형태의 인수를 취할 수 있다. 이 경우에는 인용 기호를 반드시 앞에 붙여 주어야 한다.

(import java.sql.DriverManager
        '(java.util Date Calendar)
        '(java.net URI ServerSocket))
Note

자바 라이브러리를 불러다 쓰다 보면, 패키지명은 다르지만 클래스명이 우연히도 같은 경우가 있다. 그런데 import 함수는, require`나 `use 함수와는 달리, alias 기능(:as)을 제공하지 않는다. 그래서 이런 경우에는, 댜음의 예에서처럼 자주 쓰는 클래스는 import 함수로 불러 쓰고, 자주 쓰이지 않는 클래스는 완전한 패키지명을 붙인 형태로 호출해 주면 된다.

(import java.util.Date)

(def date (Date.))

(def sql-date (java.sql.Date. (.getTime date)))

4.2. 외부 자바 라이브러리의 클래스 import 하기

또한 다른 외부 자바 라이브러리의 클래스를 사용하려 할 때도 import를 사용한다.

(import '(org.apache.commons.lang StringUtils SystemUtils))

(StringUtils/isEmpty "")   ;=> true

물론 import하려는 자바 라이브러리는 project.clj:resource-paths에 다음과 같이 지정된 디렉토리에 있어야 한다.

:resource-paths ["src/main/jars"]

5. refer-clojure

예를 들어, 다음과 같은 코드를 작성하고 컴파일을 하면

(ns my-namespace)

(defn inc []
  "my new inc function")

my-namespace 이름공간에 새로 정의한 inc 함수가 clojure.core에 이미 정의되어 있는 inc 함수를 덮어 쓴다는 경고 메시지를 컴파일러가 내보낸다.

WARNING: inc already refers to: #'clojure.core/inc in namespace: my-namespace,
  being replaced by: #'my-namespace/inc

하지만 다음과 같이 refer-clojure를 추가하면, 위와 같은 메시지가 뜨지 않도록 컴파일러에게 미리 알려주는 역할을 수행한다.

(ns my-namespace
  (:refer-clojure :exclude [inc]))

(defn inc []
  "my new inc function")

결과적으로 다음 두 함수는 같은 일을 수행한다.

(refer-clojure :exclude [inc])

(refer 'clojure.core :exclude [inc])
Note

참고로 이 함수는 ns 매크로 안에서 실행해야만 효과가 있다. 다음과 같이 실행하면 (1)의 단계에서 기본적으로 (refer-clojure)가 이미 실행되어서, (2)를 실행한다 해도 그 효과가 발생하기에는 때가 너무 늦기 때문이다.

(ns my-namespace)                ;(1)

(refer-clojure :exclude [inc])   ;(2)

(defn inc []
  "my new inc function")

따라서 refer-clojurerefer 함수에서 사용할 수 있는 필터들을 모두 사용할 수 있다.

(:refer-clojure :exclude [print])

(:refer-clojure :only [print])

(:refer-clojure :rename {print core-print})

6. ns

ns 매크로는 지금까지 설명한 함수들(단, refer 함수는 제외)을 모두 사용할 수 있도록 감싸 만든 매크로이다. 차이점은 각 함수 이름 앞에 콜론(:) 기호를 붙여 주어야 하고, 뒤따르는 이름공간 앞에 인용(') 기호를 붙일 필요가 없다는 정도이다. ns 매크로 안에서 인용 기호를 붙이면 오히려 에러가 발생한다.

(ns foo.bar
  (:refer-clojure :exclude [find])
  (:require [clojure.string :as string]
            [clojure.set :refer [difference intersect]]
            :verbose)
  (:use clojure.test :reload)
  (:import java.util.Date
           [java.util.concurrent Executors TimeUnit]))

7. create-ns/in-ns/ns 비교

다음에 create-ns, in-ns, ns 간의 차이점을 비교 설명한다.

create-ns 함수는 인수로 주어진 이름공간이 존재하지 않으면 새로 생성한 후 그 이름공간을 반환한다. 이미 존재하면 단순히 그 이름공간을 반환한다. 아래의 예제에서 user> 프람프트가 변경되지 않은 것에 주목하자.

user> (create-ns 'my-new-namespace)
#namespace[my-new-namespace]

user>

in-ns 함수는 create-ns 함수가 하는 일에 더해, 현재의 이름공간을 해당 이름공간으로 변경한다. 아래의 예제에서 user> 프람프트가 my-new-namespace>로 변경된 것에 주목하자.

user> (in-ns 'my-new-namespace)
#namespace[my-new-namespace]

my-new-namespace>

하지만 이 상태에서는 클로저 코어에 정의되어 있는 + 함수조차 실행되지 않는다. 그래서 + 함수를 실행하려면 앞에 clojure.core 이름공간을 앞에 붙여 주어야 한다.

my-new-namespace> (+ 5 6)
CompilerException java.lang.RuntimeException: Unable to resolve symbol: + in this context

my-new-namespace> (clojure.core/+ 5 6)
11

하지만, 클로저의 특수 형식들(special forms)은 실행 가능하다. 다음은 특수 형식인 def가 실행된 예이다.

my-new-namespace> (def a 10)
#'my-new-namespace/a

my-new-namespace> a
10

이에 반해, ns 매크로는 in-ns 함수가 하는 일에 더해, refer-clojure 함수를 내부적으로 호출해 주므로 clojure.core 이름공간에 정의되어 있는 모든 것들을 그대로 이용할 수 있게 된다.

user> (ns my-new-namespace)
nil

my-new-namespace> (+ 2 3)
5

그래서 in-ns 함수의 주요 용도는, 아래의 예에서처럼, 이미 ns 매크로로 만들어진 이름공간 사이를 REPL에서 이동하기 위한 목적으로 주로 사용된다.

user> (ns my-new-namespace)
nil

my-new-namespace> (in-ns 'user)
#namespace[user]

user> (+ 1 2)
3

user> (in-ns 'my-new-namespace)
#namespace[my-new-namespace]

my-new-namespace> (* 4 5)
20

in-ns 함수의 또다른 용도는, 하나의 이름 공간으로 많은 기능을 제공하고 싶을 때이다. 예를 들어, 하나의 이름공간에 제공하고자 하는 코드가 10,000 라인에 이른다고 가정해보자. 이것을 더 작은 이름 공간 단위로 나누어 제공하는 것이 더 바람직하겠지만, 경우에 따라서는 하나의 이름 공간으로 제공하고 싶은 경우도 있을 수 있다[1]. 이런 경우 하나의 파일 안에 모든 코드를 작성해 놓으면 코드의 관리와 유지/보수가 어려워지게 된다. 이럴 때 in-ns 함수를 load 함수와 함께 다음과 같이 이용할 수 있다.

very-huge-namespace.clj
(ns very-huge-namespace)

......
(load "small-part-1")
(load "small-part-2")
......
small-part-1.clj
(ns small-part-1)

(in-ns 'very-huge-namespace)

;; small-part-1 code here
.......
small-part-2.clj
(ns small-part-2)

(in-ns 'very-huge-namespace)

;; small-part-2 code here
.......

8. 심볼 테이블의 구성

클로저는 각각의 이름공간마다 심볼 테이블을 유지/관리한다. 그래서 프로그램 실행 중에 이 심볼 테이블에 대한 조회 및 조작이 가능하다. 그리고 이 심볼 테이블은 맵 자료형으로 구현되어 있다.

먼저 my-new-namespace 이름공간에 다음과 같이 코드를 작성한 후 실행해 보자.

(ns my-new-namespace)

(def my-public-var 10)
(def ^:private my-private-var 20)

(defn my-public-fn [a b]
  (+ a b))

(defn- my-private-fn [a b]
  (+ a b))

아래의 실행 결과에서도 확인할 수 있듯이, ns-interns 함수는 개발자가 직접 해당 이름공간에 정의한 모든 public/private '심볼과 var’로 이루어진 키/값 쌍을 반환한다. ns-publics 함수는 그 중에서 public '심볼과 var’만으로 이루어진 키/값 쌍을 반환한다.

(ns-interns 'my-new-namespace)
;=> {my-private-var #'my-new-namespace/my-private-var,
;    my-public-var #'my-new-namespace/my-public-var,
;    my-public-fn #'my-new-namespace/my-public-fn,
;    my-private-fn #'my-new-namespace/my-private-fn}

(ns-publics 'my-new-namespace)
;=> {my-public-fn #'my-new-namespace/my-public-fn,
;    my-public-var #'my-new-namespace/my-public-var}

심볼 테이블의 자세한 내용을 살펴 보기 전에, 위의 my-new-namespace를 실행했을 때의 심볼 테이블을 구성하는 모든 요소들의 각 종류 별 개수를 먼저 알아 보자.

;;Clojure 1.7.0 기준
(count (ns-map 'my-new-namespace))       ;=> 699
(count (ns-refers 'my-new-namespace))    ;=> 599
(count (ns-imports 'my-new-namespace))   ;=> 96
(count (ns-interns 'my-new-namespace))   ;=> 4
ns-map   =   ns-refers   +   ns-imports   +   ns-interns
------       ---------       ----------       ----------
 699개        599개           96개             4개

  • ns-map 함수는 심볼 테이블의 모든 구성 요소들의 키/값 쌍들의 맵을 반환한다.

  • ns-refers 함수는 ns 매크로 실행시 기본적으로 실행되는 (refer-clojure)의 결과로 refer되는 clojure.core 안의 모든 '심볼과 var’로 이루어진 키/값 쌍들의 맵을 반환한다.

    (ns-refers 'my-new-namespace)
    ;=> {comparator #'clojure.core/comparator,
    ;    sorted-map #'clojure.core/sorted-map,
    ;    send #'clojure.core/send,
    ;    drop-while #'clojure.core/drop-while,
    ;    ......}
  • ns-imports 함수는 ns 매크로 실행시 기본적으로 import되는 '심볼과 자바 클래스’로 이루어진 키/값 쌍들의 맵을 반환한다.

    (ns-imports 'my-new-namespace)
    ;=> {Thread java.lang.Thread,
    ;    StringBuffer java.lang.StringBuffer,
    ;    BigDecimal java.math.BigDecimal,
    ;    Math java.lang.Math,
    ;   ......}
  • ns-interns 함수는 개발자가 직접 해당 이름공간에 정의한 모든 'public/private 심볼과 var’로 이루어진 키/값 쌍들의 맵을 반환한다.

9. 이름공간 조회 및 제거 함수

all-ns 함수는 모든 이름공간을 반환한다.

(all-ns)
;=> (#<Namespace clojure.string> #<Namespace clojure.set>
;    #<Namespace clojure.java.io> #<Namespace clojure.java.javadoc>
;    #<Namespace clojure.walk> #<Namespace clojure.zip>
;    ......)

find-ns 함수는 이름공간 심볼을 인수로 받아, 그 이름공간이 존재하면 해당 이름공간 객체를 반환하고 없으면 nil을 반환한다.

(find-ns 'clojure.string)
;=> #<Namespace clojure.string>

(find-ns 'not-yet-exist)
;=> nil

the-ns 함수는 이름공간 객체를 인수로 받으면, 그 이름공간 객체를 다시 반환한다. 이름공간 심볼을 인수로 받을 때, 그 이름공간이 존재하면 이름공간 객체를 반환하고, 없으면 예외를 발생시킨다.

(def for-later-use (create-ns 'my-namespace))

(the-ns for-later-use)
;=> #<Namespace my-namespace>

(the-ns 'my-namespace)
;=> #<Namespace my-namespace>

(the-ns 'not-yet-exist)
;>> Exception No namespace: not-yet-exist found  clojure.core/the-ns

remove-ns 함수는 이름공간 심볼을 인수로 받아, 그 이름 공간이 존재하면 제거하고 없으면 nil을 반환한다.

(clojure.string/upper-case "abc")   ;=> "ABC"

(remove-ns 'clojure.string)

(clojure.string/upper-case "abc")
;=> ClassNotFoundException clojure.string  java.net.URLClassLoader.findClass

(remove-ns 'not-yet-exist)
;=> nil

10. 기타

*ns* 동적 var는 현재의 이름공간 객체를 담고 있다.

user> *ns*
#<Namespace user>

user> (ns new-namespace)
nil

new-namespace> *ns*
#<Namespace new-namespace>

1. 이런 대표적인 예가 clojure.pprint 이름공간이다.