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

日曜プログラミング

休日趣味でやってるプログラミング関連記事をダラダラと

Clojure での docstring 挿入位置

Clojure

defn は一番良く使うので忘れる事はないのだが、他の def 系を使う時に docstring 挿入位置*1ってどうなってたっけと手が止まる事が時々ある。どうも自分の中でイマイチ整理できてないのでここらでちょっとまとめてみた。

基本形

(def ^{:doc " これが基本 "} foo "bar")

色々な def 系

ここで書いているサンプルは基本から派生してメタ情報を明示しなくても入れられるようにできるのかをまず試し、それがダメなら基本形はどうか、と言う流れで試してる。

def

(def foo "ふー" "です")
;=> #'user/foo

dosctring として入るのは"ふー"の方。実はこれができるようになったのは Clojure 1.3 からだとか。

defn

(defn foo-fn "何かの関数" [] (println "何かの出力"))
;=> #'user/foo-fn

ns

(ns foo.bar "名前空間だよ")
nil

defmacro

(defmacro foo-macro "何かのマクロ" [] (println "何かの出力"))
;=> #'user/foo-macro

declare

予想通り declare では派生形はないみたい。

(declare "まだバインドされてない" unbound-var)
;=> ClassCastException java.lang.String cannot be cast to clojure.lang.IObj  clojure.core/with-meta (core.clj:214)

基本形は OK。

(declare ^{:doc "まだバインドされてない"} unbound-var)
;=> #'wrap-jfx.core/unbound-var

複数でも基本形が続く場合なら OK。

(declare ^{:doc "まだバインドされてない"} unbound-var
         ^{:doc "まだまだバインドされない"} unbound-var2)
#'user/unbound-var2

defonce

こちらも派生形は使えず。

(defonce once "一度きり" "なんです。")
;=> ArityException Wrong number of args (3) passed to: core$defonce  clojure.lang.Compiler.macroexpand1 (Compiler.java:6473)

基本形は OK。

(defonce ^{:doc "一度きり"} once "なんです。")
;=> #'user/once

defprotocol

(defprotocol foo-p "何かのプロトコル")
;=> foo-p

ちなみにメソッドなしでプロトコル定義できるのは 1.5 から。未だにどういう目的で使うのか良くわからないが。

defrecord, deftype

派生形は不可。

(defrecord Foo "何かのレコード" [bar])
;=> AssertionError No fields vector given.  clojure.core/validate-fields (core_deftype.clj:279)

基本形は受け付けはするものの取り出せない。

(defrecord ^{:doc "何かのレコード"} Foo [bar])
;=> user.Foo
(doc Foo)
;=> CompilerException java.lang.RuntimeException: Expecting var, but Foo is mapped to class user.Foo, compiling:

deftype も同様。

;;; プロトコルは前の foo-p を流用
(deftype FooType "何かのタイプ" [bar] foo-p)
;=> AssertionError No fields vector given.  clojure.core/validate-fields (core_deftype.clj:279)

(deftype ^{:doc "何かのタイプ"} FooType [bar] foo-p)
;=> user.FooType
(doc FooType)
;=> CompilerException java.lang.RuntimeException: Expecting var, but FooType is mapped to class user.FooType, compiling:

Java クラス生成絡みでめんどくさいのかね。例外もそんな感じのメッセージ出してるし。

defmulti, defmethod

これは元の defmulti には付けられるが、defmethod には不可。

(defmulti foo-multi "何かのマルチメソッド")
;=> #'user/foo-multi

(defmethod ^{:doc "Bar向け"} foo-multi [] "Barメソッド")
;=> CompilerException java.lang.IllegalArgumentException: Parameter declaration Barメソッド should be a vector, compiling:

definline, defstruct, definterface

この辺りは一度も使った事ないし多分この先も使う事がないと思うので未確認。

まとめ

  • 全てに派生系が用意されてるわけではない
  • 派生形が使える場合は、名前の後に挿入可能
  • 一部基本形でも挿入できないものがある

派生系が使えたら派生、使えなかったら基本形と言う形がいいのかも。def が 1.3 で使えるようになった事から今後バージョンアップで対応される、のかな?

*1:正確に言えば :doc のメタ情報挿入位置とかになるんだろうが lisp 経験者ならまあ通じるだろうと言う事でごかんべん。