トップレベルでの let,letfn を使った private 化
Clojure でコード書いてる時、^:private
な変数や関数で
行数が増えてくるとこれってどこで使ってたっけ?と思う事が増えてきた。
原始的な検索や、タグジャンプでまあ分からん事もないんだけど 行数が増えてくるとそれすらも億劫になってくる。
良く書くパターンとしては以下のようなもの。
(def ^:private local-map {:a 1 :b 2}) (defn a-val [] (:a local-map)) (defn b-val [] (:b local-map)) (defn- local-fn [arg] (some-proc arg)) (defn a-fn [arg] (-> (local-fn arg) some-a-proc)) (defn b-fn [arg] (-> (local-fn arg) some-b-proc))
^:private
な下に書くようにすればまあそれなりに分かるんだけど、
パッと見の時は同一インデントの為すぐには区別つかなかったりする。
ある時、ふと Clojure の関数ってクロージャだから環境抱え込めるんだよな、ってのを 思い出した。て事は let や letfn の中に Public Var な関数、変数定義すりゃ少しは 整理付くんじゃなかろーかと思い立った。
先ほど例示した擬似コードを書き直すとこんな感じ。
(let [local-map {:a 1 :b 2}] (defn a-val [] (:a local-map)) (defn b-val [] (:b local-map))) (letfn [(local-fn [arg] (some-proc arg))] (defn a-fn [arg] (-> (local-fn arg) some-a-proc)) (defn b-fn [arg] (-> (local-fn arg) some-b-proc)))
どうだろう。どちらの例も local-map
や local-fn
は ^:private
な事は
変わりないが、let
や letfn
で包まれる事で最初の例よりも関連付けが明確に
なってるんじゃなかろうか。
ns やファイルを分ける程の規模ではないが、共通で使われるのがせいぜい 2,3 箇所くらいな
ローカルを定義したいと言う状況では有効だと思う。もちろん ns 全体であちこちで
local として使いたい場合は素直に ^:private
付与したシンボルでいいと思う。
ただ、この手法は擬似コード例で言う local-map
, local-fn
に docstring を
入れられなくなるという明確なデメリットもあるので結局お好みで選ぶことになるのかなやっぱり。
ちなみにこれはオリジナルな話でも何でもなく、xyzzy でスクリプトを書いていた頃 こんな書き方そういやあったなと言う記憶が蘇ったもの。