Skip to content

Instantly share code, notes, and snippets.

@nfunato
Last active December 10, 2015 16:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nfunato/4462240 to your computer and use it in GitHub Desktop.
Save nfunato/4462240 to your computer and use it in GitHub Desktop.
Google Common Lisp Style Guide (a translation in Japanese)
Note:
The original document is a section of CLOS in "Google Common Lisp Style Guid",
http://google-styleguide.googlecode.com/svn/trunk/lispguide.xml (v1.17)
This translation is based on a Japanese translation
http://google-common-lisp-style-guide-ja.cddddr.org/?showone=CLOS#CLOS
as of 2013/01/05 23:00 JST
CC-By 3.0 License
総称関数が、他のモジュール(コードの他の部分)から呼び出されることになっ
ている場合、その関数の(ある特定のクラスの振る舞いに対するという意味で)
包括的な規約を説明するドキュメンテーションストリングの付けられた
DEFGENERICフォームを明示的に書くべきです。一般に、DEFGENERICフォームを
明示的に書くのは良いことですが、 モジュールのエントリポイントに対しては
必須です。
総称関数の引数リストが&KEYを含む場合、そのDEFGENERICには、受付可能な全
てのキーワード引数を常に明示的に記載すべきです。(Common Lispは、このこ
とを要求していませんが、これは理に叶ったことですし、SBCLでは不要な警告
を避けることができます。)
スロットに影響を及ぼす一切のメソッドコンビネーションを回避するという、
はっきりとした意図があるのでなければ、SLOT-VALUEやWITH-SLOTSの利用は避
けるべきです。数少ない例外には、INITIALIZE-INSTANCEメソッドと
PRINT-OBJECTメソッド、そして、通常はユーザーに見える抽象化を提供するメ
ソッドの低レベルの実装に隠蔽されているスロットへのアクセスが含ま
れます。これら以外は、WITH-ACCESSORSを使うべきです。
一般にアクセサの命名は<protocol-name>-<slot-name>という慣習に従います。
ここで"プロトコル"とは、明確に定義された振る舞いを持つ一連の関数[機能?]を
大まかに示しています。
"プロトコル"の考えを形式的に表すことは、必ずしも意図されていません。ま
してや、ファーストクラスの"プロトコル"オブジェクトを表すことはです。し
かしながら、実際にそのプロトコルを具体的に表現する抽象的なCLOSクラスや
Interface-Passing Styleのインターフェイスはありえます。そういうときに、
さらなる(下位の)クラスや(下位の)インターフェイスが、リーダやライター等
の、プロトコルに含まれる(総称)関数のためのメソッドを定義することで、プ
ロトコルの一部または全部を実装することができます。
例を挙げると、pnrと呼ばれる抽象的なプロトコルがあったとして、それは、
pnr-segmentsと、pnr-passengersいうアクセサを持つとします。 そのとき、
air-pnr、hotel-pnr、car-pnrというクラスが、pnr-segmentsや
pnr-passengersのメソッドがアクセサとして実装できることは理に叶っている
というものです。
デフォルトでは、抽象基底クラスの名前は、抽象的なプロトコルの名前として
使われるので、アクセサの名前は<class-name>-<slot-name>になります。
このような名前は広く行き渡ってはいますが、この形式は必須のものでも望ま
しいものでもありません。総じてこれは、"シンボルが膨れ上がる(symbol
bloat)"ことを助長するものであり、また、多くの場合、"トランポリン"メソッ
ドの蔓延に繋がります。
<slot-name>-ofという名前を持つアクセサは使用すべきではありません。
総称関数の定義に一つより多くのDEFMETHODが使われる(ことになると思われ
る)場合、DEFGENERICフォームを明示的に使用すべきです。理由は、ある特
定的なクラスの個々のメソッドが説明するものに対する意味で、総称関数のド
キュメンテーションは、その関数の抽象的な規約を説明するものだからです。
抽象的なプロトコルが無い場合に総称関数を使ってはいけません。より具体的
に述べるならば、もし、N番目の引数を特定化している複数の総称関数のメソッ
ド[訳注: 原文はgeneric functionの後ろにMETHODがあるべきと考えます]が
あるとすれば、特定化を行なっているのクラスは、すべてある一つのクラスの
子孫であるべきです。総称関数は、"オーバーロード"のために使ってはいけま
せん。言い換えれば、全く関連の無い型の総称関数に対して単純に同じ名前を
使ってはいけません。
より正確には、それらが実際、共通のスーパークラスの子孫なのかどうかでは
なく、同じ"プロトコル"に従うかどうか、ということです。つまり、個々のメ
ソッドに明確なDEFGENERICがあるかのように、二つのクラスは同じ総称関数
のセットを扱っているべきなのです。
別の方法で説明しましょう。AとBの二つのクラスと、総称関数Fがあり、Fには
メソッドが二つあり、これらは、渡されたAとBの型の引数でディスパッチする
ものとします。プログラム中にある一つのFの関数呼び出しについて、実行時に
その引数がAのクラスとなる場合とBのクラスとなる場合があると思われるかど
うか。もし、思われないのであれば、おそらくオーバーロードしており、一つ
の総称関数を使用すべきではありません。
このルールには一つの例外が認められます: 対応する引数が同じものを"意味"
する場合、オーバーロードしても良し、とします。典型的には、一つのオーバー
ロードではXオブジェクトを認め、他のオーバーロードではXオブジェクトの名
前(シンボルかなにかになるでしょう)を認めるというものです。
MOPのintercessory(仲裁的)オペレーション[訳注:intercessionは技術用語で、
プログラムの構成要素を調べる(introspection)ことに対して、その構成要素を
変更することを指します(出典: http://bc.tech.coop/blog/050919.html)。
このようなオペレータの例としては、defclassの内部で使われる
compute-class-precedence-listやdefmethodの内部で使われるadd-method等が
あります。]を
実行時に使用してはいけません。MOPのintercessoryオペレーションをコンパイ
ル時に使用してもいけません。実行時では、最悪の場合には危険を犯すことに
なり、良くてもパフォーマンス上の問題があります。コンパイル時では、通常、
然るべきマクロで事を正しく1パスで行う方が、2パス目が必要となる
intercessionによる対処より簡潔です。しかし、前方参照の解決するための対
処が必要なこともあるでしょう。そのときにはintercessionの利用も許されま
す。MOPのintercessionは、対話的開発では素晴らしい道具ですし、開発中とデ
バッグ中は、楽しんで使うことができるでしょうが、通常のアプリケーション
内でこれを利用するべきではありません。
クラス定義で:READER、:WRITERや:ACCESSORメソッドがつくられる場合、これら
のメソッドを再定義してはいけません。 :BEFORE、:AFTERや、:AROUNDメソッド
を追加することはOKです。 しかし、プライマリメソッドをオーバーライドして
はいけません。
キーワード引数を持つメソッドでは、常に&KEYを使わなければなりません。
たとえ、メソッドがどのキーワード引数の値にも関知しない場合であっても、
&ALLOW-OTHER-KEYSは使うべきではありません。
あるキーワードが総称関数のいずれかのメソッドで受け付けられる限り、総称
関数でそのキーワードを使うことができます。同じ総称関数の他の全てのメソッ
ドが明示的に触れなくてもです。[訳注: CLHS 3.5.1.4のことですね]
このことは、特にINITIALIZE-INSTANCEメソッドで重要です。ひとたび
&ALLOW-OTHER-KEYSを使ったならば、ミススペルであろうと、間違ったキーワー
ドであろうと、MAKE-INSTANCEの呼び出しでのエラーチェックを無効にしてしま
うからです!
典型的なPRINT-OBJECTメソッドは、下記の様になるでしょう:
(defmethod print-object ((p person) stream)
(print-unprintable-object (p stream :type t :identity t)
(with-slots (first-name last-name) p
(safe-format stream "~a ~a" first-name last-name))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment