require と use

 Clojureで書かれた外部コードを参照する時、require や use を使います。ちょっとまとめてみました。使用しているClojureは 1.2.0-master-SNAPSHOTです。

require と use の違い

 例えば、clojure.pprint 名前空間に定義されている、pprint という関数を使う場合。(clojure-version は組み込みのグローバル変数です)。

;; カレント名前空間に clojure.pprint (clojure1.1では clojure.contrib.pprint)
;; で定義されている関数や変数を導入
(use 'clojure.pprint)
(pprint *clojure-version*)
;; clojure.pprint 名前空間を指定してモジュールを読み込む
(require 'clojure.pprint)
(clojure.pprint/pprint *clojure-version*)       

 どちらも、clojure.pprint モジュール(名前空間)を参照するための式です。use は現在の名前空間に pprint という関数を導入します。require は指定した名前空間の階層構造を保ったままモジュールを読み込みます。ですので、pprint 関数は clojure.pprint 名前空間名で修飾しなければ参照できません。
 use で読み込むと修飾無しで pprint と書ける利点がありますが、現在の名前空間を汚染してしまう欠点があります。C++ の using namespace 名前空間名; と似ています。
 逆に、require はコードが長くなる欠点がありますが、名前空間を汚さないという利点があります。
 clojure.core や clojure.contrib で定義されている識別名は、重複するものがほとんど無くドキュメントも整備されているので use で読み込んでも問題はあまり起らないと思いますが、自作モジュールやサードパーティ製モジュールでは名前の衝突が発生する可能性があります。名前衝突が無くても、名前空間をつけた方が関数や変数の意味がわかりやすい、という事もあるでしょう。 そんな時は require を使った方がよいです。

まとめて参照

 require (と use) は複数の名前空間をとることが出来ます。

(require 'clojure.contrib.classpath)
(require 'clojure.contrib.macro-utils)

 は

(require 'clojure.contrib.classpath
         'clojure.contrib.macro-utils)

 でもよいです。
 さらに共通のprefixをもつ名前空間はまとめて記述することができます。上の記述は次のように書いても OK です。(use でも同様)

(require '(clojure.contrib classpath macro-utils)) 

追記:prefixを分離して書くやりかたは、参照モジュールが一つだけでも使えます。
つまり、次のふたつは同じ意味になります。

(require 'clojure.contrib.macro-utils)
(require '(clojure.contrib macro-utils))

この二つは同じ挙動になります。

参照する識別子を制限するキーワード :only

 clojure.pprint 名前空間には pprint 関数以外にも沢山の関数や変数が定義されています。前述の方法では clojure.pprint 内の全ての識別子を導入してしまいますが、特定の識別子だけ参照することもできます。

;; pprint関数 と pp関数 の二つだけを参照
(use '[clojure.pprint :only (pprint pp)])

 :only を使うと、参照する識別子をコントロールできるので、use による名前空間汚染を最小限に抑えることができます。利用する識別子が明示されるのも利点になるでしょう。
 :only が使えるのは use だけのようです。require では使えません。 (訂正:requireでも使えます)

追記 :only キーワードには、少々わかりにくい仕様があります。次の3つの式を見てください。

(use '[clojure.pprint :only (pprint pp)])   ; OK    ---[1]
(use '(clojure.pprint :only (pprint pp)))   ; ERROR ---[2]

(use '(clojure (pprint :only (pprint pp)))) ; OK    ---[3]

「まとめて参照」の項の「追記」に書いたように、名前空間の指定方法は2通りあります。したがって、:only の例も2通りの書き方があるのですが、名前空間を分断しない [1] の書き方の時だけ、vector で記述しなければならない仕様のようです。[2] のように list で書くとエラーになります。[3] の名前空間の最後の識別子を分離する書き方では、どちらでも OK です。

名前空間に別名を付ける :as

 たとえば次の3つの名前空間で構成されたプロジェクトを作ったとします。

strike-witches.core
strike-witches.eila-ilmatar-juutilainen
strike-witches.alexandra-urajimirovuna-ritovuyaku

 core 以外の 2つの名前空間にはそれぞれ get-prof という同名関数があるとします。その両方を core で参照したい場合、次のようには書けません。

;; core モジュールのコード (NGの例)
(ns strike-witches.core)

(use '[strike-witches.eila-ilmatar-juutilainen :only (get-prof)])
(use '[strike-witches.alexandra-urajimirovuna-ritovuyaku :only (get-prof)])

(println (get-prof)) ;; どっちの get-prof か判別できないので ERROR

 同名関数を使う場合、require で名前空間階層をもったまま参照する必要があります。

;; core モジュールのコード (これなら OK)
(ns strike-witches.core)

(require 'strike-witches.eila-ilmatar-juutilainen)
(require 'strike-witches.alexandra-urajimirovuna-ritovuyaku)
         
(println (strike-witches.eila-ilmatar-juutilainen/get-prof))    
(println (strike-witches.alexandra-urajimirovuna-ritovuyaku/get-prof))

 これで一応プログラムは正常動作できます。が、流石にこの名前空間をすべての関数呼び出しには書きたくないですね。そこで別名(alias)を付けるための構文が用意されています。

;; core モジュールのコード (これも OK)
(ns strike-witches.core)

(require '(strike-witches (eila-ilmatar-juutilainen :as eila)))
(require '(strike-witches (alexandra-urajimirovuna-ritovuyaku :as sanya)))
         
(println (eila/get-prof))    
(println (sanya/get-prof))

 同じ prefix をまとめて書くやりかたを使えば、require を一つにまとめる事もできます。

(require '(strike-witches (eila-ilmatar-juutilainen :as eila)
                          (alexandra-urajimirovuna-ritovuyaku :as sanya)))

 :as は use でも一応つかえますが、そもそも use の場合、名前空間修飾無しで参照するので、:as 使う意味は無いです。
追記 :as でも、:only と同じ注意点があります。

(require '[strike-witches.eila-ilmatar-juutilainen :as eila])  ; OK    [1]
(require '(strike-witches.eila-ilmatar-juutilainen :as eila))  ; ERROR [2]

(require '(strike-witches (eila-ilmatar-juutilainen :as eila))) ;OK    [3]

この場合にも、[2] の書き方はエラーです。

丸括弧か四角括弧か?

 require と use の式の中に記述する括弧は list の丸括弧でも、vector の四角括弧でもどちらでもよいみたいです。

;; 括弧は自由
(require '(strike-witches (eila-ilmatar-juutilainen :as eila)))
(require '[strike-witches (eila-ilmatar-juutilainen :as eila)])
(require '(strike-witches [eila-ilmatar-juutilainen :as eila]))
(require '[strike-witches [eila-ilmatar-juutilainen :as eila]])
;;どれでも OK

追記:既に述べたように、:only や :as を使うとき、ある条件下で list が使えない場合があります。
詳細は、前2項の「追記」を御参照下さい。

再読み込みフラグ :reload :reload-all

 :reload :reload-all は既に読み込み済みのモジュールを強制的に再読み込みさせます。このフラグが無いと、読み込み済みモジュールをreloadしないため、プログラム作成中にコードの修正をしても動作に反映されなかったりします。
 :reload は参照するモジュールのみ強制reload, :reload-all は参照するモジュールがさらに参照している先のモジュールもreloadしてくれます。通常は :reload-all の方を使った方がよいでしょう。

;; 前後、どっちに書いても OK
(require :reload-all '(strike-witches (eila-ilmatar-juutilainen :as eila)))
;; or
(require '(strike-witches (eila-ilmatar-juutilainen :as eila)) :reload-all)

詳細表示フラグ :verbose

 未確認ですが、読み込み情報を表示するらしいです。

nsマクロ内での記述

 require と use の指定は、名前空間を規定する nsマクロの引数で指定することもできます。実際のプログラムではこちらの形式を使うことになるでしょう。

(ns strike-witches.core
  (:use
   [clojure.pprint :only (pprint pp)]
   (clojure.contrib
    (classpath :only (classpath))
    (macro-utils :only (macrolet))))
  (:require
   :reload-all
   (strike-witches
    (eila-ilmatar-juutilainen :as eila)
    (alexandra-urajimirovuna-ritovuyaku :as sanya))))

 これまで説明してきた、require と use は関数でしたが、nsマクロの引数では :require と :use というキーワードで記述します。マクロ引数なので、require や use のように引数をクォートする必要はありません。
 記述方法は基本的にこれまで説明してきた書き方に準じます。
 なお、ここでは、:require と :use を一つづつ使い、複数の参照をまとめて記述していますが、:require, :use を複数に分けて書くこともできます。そのほうが見やすいかもしれません。

追記::as と :only を両方適用する

記述ルールはこれまでのルールの応用です。

;; require関数を使う場合
(require
 '(strike-witches (eila-ilmatar-juutilainen :as eila :only (get-prof))
                  (alexandra-urajimirovuna-ritovuyaku :as sanya :only (get-prof))))
;; nsマクロ内で書く場合
(ns strike-witches.core
  (:require
   :reload-all
   (strike-witches
    (eila-ilmatar-juutilainen :as eila :only (get-prof))
    (alexandra-urajimirovuna-ritovuyaku :as sanya :only (get-prof)))))