フックポイントとしての総称関数

プログラミング言語 Scheme の仕様にはオブジェクト指向的な支援が含まれない。 しかし、独自の拡張として持っている場合もある。 あるいは後付けでオブジェクト指向的な機能を追加するライブラリもある。 オブジェクト指向というのも様々な種類があるが、 Scheme で使われることが多いのは Common Lisp に含まれるオブジェクトシステム (CLOS) を真似たものであることが多い。 もちろん前提となる言語が違うのであるから、 Scheme の、あるいは処理系の都合に合わせて改変が加えられてはいる。

Scheme 処理系のひとつである Gauche もまた CLOS 風のオブジェクトシステムを持っていて様々に活用されている。 Gauche を使っていて特に頻繁に使われる総称関数 (Scheme の用語では「関数」よりも「手続き」が使われるが、 CLOS の習慣に倣ってかドキュメントには「総称関数」という用語が用いられている) は ref だろうか。 ref は複数の要素をまとめるような型のオブジェクトに対して同じような使い勝手で適用でき、要素を抜き出すものだ。

(ref '(a b c d) 2)
(ref '#(e f g h) 3)
(ref "ijkl" 1)

このように ref はリスト、ベクタ、文字列に使える。 その他にもハッシュテーブルやツリーマップなどにも対応している。 必要であればメソッドを追加して新しい型に対応させることも出来る。

ref の場合は同じような意味を持つ手続きをひとつの名前にまとめたというだけのものだが、アプリケーションに機能を追加するためのフックポイントとしても活用できる。 たとえば先日に紹介した Gauche-zip-archive という拡張において現状では zip-add-entry だけは総称関数として提供していて、これはアーカイブにエントリを追加するにあたって前後に処理を追加したい場合を想定しているからだ。 例として、エントリの名前に必ず拡張子 .txt を追加するというような処理を入れたい場合にはこう書ける。

(use zip-archive)

(define-class <extended-zip> (<output-zip-archive>)
  ())

(define-method zip-add-entry
    ((archive <extended-zip>) (name <string>) (content <string>))
  (next-method archive (string-append name ".txt") content))

(let ((archive (make <extended-zip> :name "test.zip")))
  (zip-add-entry archive "entry1" "hoge-huga-hige")
  (zip-close archive))

このようなカスタマイズ方法は、 Gauche で書かれている Wiki システムである WiLiKi でも活用されている。 私はそれを見て Gauche-zip-archive にも導入してみた次第である。 現時点では ZIP フォーマットのごく基本的な部分にしか対応していないライブラリではあるが、メソッドの追加という形で ZIP フォーマットの様々な拡張に対応できる可能性を提供したつもりだ。 (自分ではやりたくないのでという本音。)

Document ID: 21bf69adf19ce4d9fa17f6c7ad405d68