名前空間付きキーワードの意味と使い方

Clojureにおいて,キーワードは多用されます.実は,キーワードには名前空間(namespace qualifier)をつけることができます.従来はそれほど重要性は高くありませんでしたが,Clojure 1.9におけるcore.specの導入により,にわかに重要性が高まりました.機能自体は以前から存在していますが,Spec Guideを読んではじめてqualifiedキーワードの登場に面食らった人も多いのではないでしょうか.

このエントリーでは,qualifiedキーワードの記法についてまとめ,次にcore.specにおける利用方法,最後にcore.spec以外での利用方法についてまとめます.

名前空間付きキーワードの記法

Clojureにおいては,一般のvarが名前空間に属するのと同様にキーワードも名前空間に属することができます.名前空間付きキーワードは, :namespace/foo という文法で記述することができます.qualifiedキーワードは,unqualifedキーワードとは違うものとして扱われます.また,違う名前空間に属する場合は異なるキーワードとして扱われます.

user=> :my-ns/foo
:my-ns/foo

user=> (= :foo :my-ns/foo) ;; unqualified / qualified キーワードは互いに等価でない
false
user=> (= ::foo :foo) 
false

user=> (= :my-ns2/foo :my-ns/foo) ;; 名前空間が違う場合は等価でない
false

簡略化の記法として,現在の名前空間に属するキーワードは ::fooのように書くことができます

user=> ::foo
:user/foo

user=> (= ::foo :user/foo)
true

また,Clojure 1.9 alpha-8 から導入された省略記法として,#:記法があります.mapのキーにキーワードが使われている場合に記述を省略できます(alphaリリースなので将来消される可能性もありますが,alpha-10の時点では残っています).

;; 1.9 alpha-10でテスト
user=> #:my-ns { :key "val" }
#:my-ns{:key "val"}

user=> (def my-map #:my-ns{:key "val"} ;; my-mapのキーは :my-ns/key である
#’user/my-map

user=> (:key my-map)
nil

user=> (:my-ns/key my-map)
"val"

core.specにおける利用法

core.specにおいては,core.spec/def を用いてspecをキーワードとひも付けてregistryに登録します.

;; http://clojure.org/guides/spec より引用
(s/def ::date inst?)

ここではキーワードを登録のキーとして使っており,そして(おそらく)registryはグローバルに管理されているので,unqualifiedキーワードを使ってしまうと衝突の危険があります.なので,registryのキーとしてキーワードを指定する際は必ずqualifiedなキーワードを使うべきです.普通は現在の名前空間に登録するでしょうから,「Spec Guide」では一貫して::記法を利用した qualifiedキーワードが使われています.

core.spec以外での使用法

はっきりいって,通常の record の使用の際にqualifiedキーワードを使うことはそれほど多くないでしょう.

有用なのは,ライブラリ同士or名前空間同士で意図しない衝突を避けたい場合です.[3]では,re-frameというclojurescriptフレームワークで,イベントハンドラの名前(=キーワード)が名前空間同士で衝突しないようにqualifiedキーワードを使うことが述べられています.

[2]では,Ring v0.1ではリクエストとレスポンスのmapにqualifiedキーワードが使われていたけれどもv0.2ではunqualifiedに変更されたという経緯についても触れられています.

参考文献