読者です 読者をやめる 読者になる 読者になる

(define -ayalog '())

括弧に魅せられて道を外した名前のないプログラマ

defn- のような def- を定義したけど、 private にならないという話

ML で読んでほえーと思った。

Why (defn- ...) but (def ^:private? ...)

元の質問者は def フォームは ^:dynamic? になるの?って聞いてますが、何故かというと彼の書いたマクロ def- が綺麗に動かなかったからです。
こういう感じ。

(defmacro def-
  "Why (defn- private-fn ...) but (def ^:private var ...)?"
  [sym & body]
  `(def ^:private ~sym ~@body))

(macroexpand '(def- blah "foo bar quux")) ;=> (def blah "foo bar quux")

という風にマクロ展開すると ^:private が消えちゃってますね。ちなみにこれ定義後に meta 情報を見ても同様に private のメタ情報は消えています。

(meta #'user/blah) ;=> {:ns #<Namespace user>, :name blah, :column 1, :line 1}

これに対する回答はすごく単純でリーダーマクロは読み込み時に評価されるのであってマクロ展開時じゃないよ、ということ。
つまり ^:private はマクロで操作出来るシンタックスではなくて、マクロで評価するときには存在しないものとなっているということなんですね。
なので、 defn- のような def- を定義したいなら defn- のソースを参考にしたほうがいいよと言われています。

(defmacro defn-
  "same as defn, yielding non-public def"
  {:added "1.0"}
  [name & decls]
    (list* `defn (with-meta name (assoc (meta name) :private true)) decls))

これを受けて def- を定義しなおすならこうなりますと。

(defmacro def-
  "same as def, yielding non-public def"
  ([name] `(def- ~name nil))
  ([name expr]
   (list `def (with-meta name (assoc (meta name) :private true)) expr)))

何がリーダーマクロなのかとかちゃんと理解してないとこういうところでハマるんですね :)