trivial-gray-streams についてのきわめて瑣末な記事

Gray Stream の提案と,その実装であるところの trivial-gray-streams について訳したよ.

まずはGray Streamsについて.参考サイトは以下のとおり.
http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html

FAILED Issue STREAM-DEFINITION-BY-USER ("Gray Streams")/ユーザ定義ストリーム(Gray Streams)

Issue STREAM-DEFINITION-BY-USER
参照 CLtL pages 329-332, 378-381, and 384-385.
関係する Issue STREAM-INFO, CLOSED-STREAM-FUNCTIONS, STREAM-ACCESS, STREAM-CAPABILITIES
Category ADDITION
Edit history Version 1, 22-Mar-89 by David N. Gray
Status の結果,標準には含まれないことになりました.

問題

Common LISP はユーザが,標準のI/O関数と使えるような独自のストリームを定義するための標準の方法を提供していない.このことは,Common LISPの標準のウィンドウシステムを開発するのを邪魔すると思われる.というのも,Common LISPには標準のI/Oがあり,また標準ウィンドウシステムの端緒があるが,それらを接続してポータブルなウィンドウシステムを作るためのポータブルな方法がないからである.

さらに,多くのアプリケーションでユーザは独自のフィルタストリーム,たとえばプリンタデバイスコントロール,レポート整形,文字コード変換,暗号化/解読とのためのストリームを定義したいと思われる.

提案 STREAM-DEFINITION-BY-USER:GENERIC-FUNCTIONS

概要

I/Oを処理するための総称関数の集合を定義する.それらの総称関数はストリームの引数によって特定されるメソッドを持っていて,既存のI/O関数もそれを使うようにする.ユーザは自分が定義したストリームがサポートされるように,総称関数にメソッドを追加することができる.

デフォルトのメソッドを書くために,ストリームのスーパークラスとなるべきクラスの集合も定義する.

クラス

下記のクラスはユーザ定義ストリームのスーパークラスとなるべきクラスである.これらのクラスは直接インスタンス化されることを想定しておらず,デフォルトのメソッドを提供するために使われる.

FUNDAMENTAL-STREAMがトップのクラス.これを直接に継承する FUNDAMENTAL-INPUT/OUTPUT-STREAM と, FUNDAMENTAL-CHARACTER/BINARY-STREAM の4つのクラスがあり,4つのクラスのうち2つを同時に継承する FUNDAMENTAL-INPUT/OUTPUT-CHARACTER/BINARY-STREAM という4つのクラスがある.

FUNDAMENTAL-STREAM FUNDAMENTAL-INPUT-STREAM FUNDAMENTAL-OUTPUT-STREAM
FUNDAMENTAL-CHARACTER-STREAM FUNDAMENTAL-CHARACTER-INPUT-STREAM FUNDAMENTAL-CHARACTER-OUTPUT-STREAM
FUNDAMENTAL-BINARY-STREAM FUNDAMENTAL-BINARY-INPUT-STREAM FUNDAMENTAL-BINARY-OUTPUT-STREAM
FUNDAMENTAL-STREAM [Class]

このクラスは STREAM および STANDARD-OBJECT のサブクラスである.STREAMP はこのクラスを継承しているインスタンスに対して真を返す.(他のものにも true を返すことがありえる)

FUNDAMENTAL-INPUT-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.これを継承していると INPUT-STREAM-P が真を返す.

FUNDAMENTAL-OUTPUT-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.これを継承していると OUTPUT-STREAM-P が真を返す.双方向ストリームは FUNDAMENTAL-INPUT-STREAM と FUNDAMENTAL-OUTPUT-STREAM の両方を継承することになる.

FUNDAMENTAL-CHARACTER-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.STREAM-ELEMENT-TYPE のためのメソッドを提供し,そのメソッドは結果として CHARACTER を返す.

FUNDAMENTAL-BINARY-STREAM			[Class]

FUNDAMENTAL-STREAM のサブクラスである.このクラスを含めたインスタンス化可能なクラスは,STREAM-ELEMENT-TYPE メソッドを定義する必要がある.

FUNDAMENTAL-CHARACTER-INPUT-STREAM		[Class]

FUNDAMENTAL-INPUT-STREAM と fundamental-CHARACTER-STREAM を継承する.文字の入力のための総称関数のデフォルトの実装を提供する.

FUNDAMENTAL-CHARACTER-OUTPUT-STREAM		[Class]

FUNDAMENTAL-OUTPUT-STREAM と FUNDAMENTAL-CHARACTER-STREAM を継承する.文字の出力のための総称関数のデフォルトの実装を提供する.

FUNDAMENTAL-BINARY-INPUT-STREAM		[Class]

FUNDAMENTAL-INPUT-STREAM と FUNDAMENTAL-BINARY-STREAM を継承している.

FUNDAMENTAL-BINARY-OUTPUT-STREAM		[Class]

FUNDAMENTAL-OUTPUT-STREAM と FUNDAMENTAL-BINARY-STREAM を継承している.

文字入力

文字入力ストリームは, FUNDAMENTAL-CHARACTER-INPUT-STREAM を継承したクラスを定義し,以下の総称関数にメソッドを追加することで実装できる.

STREAM-READ-CHAR  stream			[Generic Function]

ストリームから1つの文字を読む.この関数は character オブジェクトか,ファイル終端に達したらシンボル :EOF を返す.FUNDAMENTAL-CHARACTER-INPUT-STREAM のサブクラスは必ずこの総称関数のためのメソッドを定義しなくてはならない.

すべての総称関数について, stream はストリームオブジェクトでなければならず,T や NIL は使えないことに注意.

STREAM-UNREAD-CHAR  stream  character		[Generic Function]

最後の STREAM-READ-CHAR の呼び出しを UNREAD-CHAR のようになかったことにする.NILを返す.FUNDAMENTAL-CHARACTER-INPUT-STREAM のサブクラスは必ずこの総称関数のためのメソッドを定義しなくてはならない.

STREAM-READ-CHAR-NO-HANG  stream		[Generic Function]

この関数は READ-CHAR-NO-HANG を実装するために使える.この関数は文字, NIL, あるいは :EOF を返す. NIL が返されるのは,入力が使えなくなった時である.:EOF を返すのは,ファイルの終端に達した時である.FUNDAMENTAL-CHARACTER-INPUT-STREAM によって提供されるデフォルトのメソッドは,単純に STREAM-READ-CHAR を呼びだす.このことはファイルストリームにおいては十分だが,インタラクティブなストリームは再定義をする必要がある.

STREAM-PEEK-CHAR  stream			[Generic Function]

peek-type 引数が nil であるような PEEK-CHAR を実装するために使われる.

この関数は CHARACTER あるいは :EOF を返す.デフォルトのメソッドは STREAM-READ-CHAR を呼び, STREAM-UNREAD-CHAR を呼ぶ.

STREAM-LISTEN  stream				[Generic Function]

LISTEN によって使われる.真あるいは偽を返す.デフォルトのメソッドは STREAM-READ-CHAR-NO-HANG を使い, STREAM-UNREAD-CHAR を使う.ほとんどのストリームは独自のメソッドを用意する必要がある.というのも,それは通常些細な事で,常にデフォルトのメソッドより効率的と思われるからである.

STREAM-READ-LINE  stream			[Generic Function]

READ-LINE によって使われる.文字列が第一の返り値として返される.第二の返り値は,文字列が行終端文字で終わっていれば真,ファイル終端に達した場合は偽を返す.デフォルトのメソッドは STREAM-READ-CHAR を繰り返し呼んでいる.

  STREAM-CLEAR-INPUT  stream			[Generic Function]

CLEAR-INPUT を実装する.NILを返す.デフォルトのメソッドはなにもしない.

文字列出力

文字列出力ストリームは, FUNDAMENTAL-CHARACTER-OUTPUT-STREAM を継承したクラスと,下記の総称関数のためのメソッドを定義することで作成できる.

STREAM-WRITE-CHAR  stream character		[Generic Function]

文字をストリームに書き出し,その文字を返す.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM のサブクラスは,この総称関数のためにメソッドを定義する必要がある.

STREAM-LINE-COLUMN  stream			[Generic Function]

この関数は次の文字が書き込まれるカラム数を返す.このストリームにおいて意味を成さない場合,NILを返す.行の始めのカラムは0である.この関数は PPRINT および FORMAT の ~T 指示子によって使われる.文字出力ストリームを定義する時はこの総称関数のためのメソッドを定義する必要があるが,それは常に nil を返すものであってもよい.

STREAM-START-LINE-P  stream			[Generic Function]

この関数は述語であり,ストリームが行の初めにあるときにT,そうでないときNILを返す.このこの関数のためのメソッドは,いつでもNILを返すものであってもよい.この関数は FRESH-LINE の実装に使われる.注意すべきなのは, STREAM-LINE-COLUMN が0を返す場合もまた行の始まりを意味するが,STREAM-START-LINE-P が意味を持つのに, STREAM-LINE-COLUMN はそうでない場合がありうるということである.たとえば,可変幅の文字を使うウィンドウなどでは,行の桁の意味ははっきりしないが,行の始まりについては明白である.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM による STREAM-START-LINE-P のデフォルトの実装ではSTREAM-LINE-COLUMN を使っている.そのため,もし STREAM-LINE-COLUMN が NIL を返すように定義されているのならば,STREAM-START-LINE-P か STREAM-FRESH-LINE のいずれかのためのメソッドが提供されるべきである.

STREAM-WRITE-STRING stream string &optional start end [Generic Function]

この関数は WRITE-STRING によって使われる.この関数は,文字列をストリームに書き出す.start と end がしていされていれば,文字列からその範囲が切りだされる.そのデフォルトの値は 0 および NIL である.文字列引数が返り値となる.FUNDAMENTAL-CHARACTER-OUTPUT-STREAM によって提供されるデフォルトのメソッドは,STREAM-WRITE-CHAR を繰り返し呼び出す.

STREAM-TERPRI  stream				[Generic Function]

TREPRI 同様に行集端子を書き出し,NILを返す.デフォルトのメソッドは (STREAM-WRITE-CHAR stream #\NEWLINE) を呼び出す.

STREAM-FRESH-LINE  stream			[Generic Function]

FRESH-LINEによって使われる.デフォルトの実装はSTREAM-START-LINE-P と STREAM-TERPRI を呼び出す.

STREAM-FINISH-OUTPUT  stream			[Generic Function]

FINISH-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-FORCE-OUTPUT  stream			[Generic Function]

FORCE-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-CLEAR-OUTPUT  stream			[Generic Function]

CLEAR-OUTPUT を実装する.デフォルトメソッドは何もしない.

STREAM-ADVANCE-TO-COLUMN  stream column	[Generic Function]

次の文字が特定のカラムに書き込まれるように,十分な空白を書き込む.成功した場合は真を返し,サポートされていない場合は NIL を返す.PPRINT および FORMAT 関数の ~T によって使われる.デフォルトメソッドは, STREAM-LINE-COLUMN と, #\Space を引数としたSTREAM-WRITE-CHAR の繰り返しの呼び出しによって実現され,STREAM-LINE-COLUMN が NIL を返した場合は NIL を返す.

その他の関数.
CLOSE  stream &key abort			[Generic Function]

現在の CLOSE 関数は総称関数によって再定義されるが,振る舞いは同じである.FUNDAMENTAL-STREAM によって実装されているメソッドは, OPEN-STREAM-P のためのフラグを立てる. CLOSE の値は Issue CLOSED-STREAM-OPERATIONS によって決定される.

OPEN-STREAM-P stream				[Generic Function]

この(提案 STREAM-ACCESS から取り入れた)関数は総称的に定義される.デフォルトメソッドは FUNDAMENTAL-STREAM によって提供され,CLOSE が呼ばれていないストリームであれば true を返す.

STREAMP  object				[Generic Function]
INPUT-STREAM-P  stream			[Generic Function]
OUTPUT-STREAM-P  stream			[Generic Function]

ユーザに STANDARD-OBJECT のサブクラスでないストリームを定義することを可能にしたい処理系は,これらのすでに存在する述語を,総称関数として定義することもできる.通常, FUNDAMENTAL-INPUT-STREAM および FUNDAMENTAL-OUTPUT-STREAM によって提供されるデフォルトメソッドで十分である.ここで,たとえば, (INPUT-STREAM-P x) は (TYPEP x 'FUNDAMENTAL-INPUT-STREAM) は等しくないことに注意すべきである.というのも,もしこれらの関数が総称関数にする以外の方法で処理系はストリームを定義するための定義するための独自の方法を提供しているかもしれないからだ.

STREAM-ELEMENT-TYPE  stream			[Generic Function]

現在の関数は総称関数によって再定義されるが,振る舞いは変わらない.FUNDAMENTAL-CHARACTER-STREAM が提供するデフォルトメソッドは CHARACTER を返す.

PATHNAME と TRUENAME はいずれも総称関数として定義しても良い.デフォルトメソッドは存在しない.すべてのストリームに対する適当な実装が考えられないためである.

バイナリストリーム.

バイナリストリームは FUNDAMENTAL-BINARY-INPUT-STREAM と FUNDAMENTAL-BINARY-OUTPUT-STREAM のどちらか,あるいは両方を継承したクラスであり,総称関数 STREAM-ELEMENT-TYPE のためのメソッドと,次の総称関数のうちどちらか,あるいは両方のためのメソッドを提供している.

STREAM-READ-BYTE  stream			[Generic Function]

READ-BYTE によって使われ,整数値を返す.ファイル終端に達したときは :EOF を返す.

STREAM-WRITE-BYTE stream integer		[Generic Function]

WRITE-BYTE を実装する. integer をストリームに書き込むとともに,結果として返す.

論理的根拠

すでにある I/O 関数は総称的にすることができない.というのも,ほとんどのケースで,ストリーム引数はオプショナルであり,そのためストリームによってメソッドを特定することができないからである.そのため,すでにある関数によって使われるより低レベルな総称関数を定義する必要がある.また, PRINT-OBJECT の第二引数によってメソッドを特定するのも適切ではない.というのも,それは高レベルな関数だからである.この関数はその第一引数が文字であれ文字列であれ, *PRINT-ESCAPE* に従ってフォーマットする必要がある.

その意味するところを明確にするために,総称関数の名前は,総称関数でない関数の名前の前に, "STREAM-" というプレフィックスをつけたものになっている.

eof-error-p と eof-value 引数を持つ高レベルな関数とともにファイルの終わりで :EO を返す総称関数を持つことは,総称関数のインタフェースを単純にし,引数を引き渡す必要をなくすことで効率的にする.この慣習に従うストリームは文字あるいは整数のみを返すことに注意すべきである.そのため, :EOF が返されることに曖昧さはない.

関数 STREAM-LINE-COLUMN, STREAM-START-LINE-P, および STREAM-ADVANCE-TO-COLUMN は捨てられた提案である STREAM-INFO が蘇ったようにみえるかもしれないが,ここでのモチベーションは異なっている.このインタフェースはユーザ定義ストリームが PPRINT や FORMAT ~T に渡せる時に必要であり,それは,これらの関数がシステム定義ストリーム上でこれらを呼べるかとは別の問題である.


こっから trivial-gray-streams

Gray Streamでした.これは提案されたものの仕様には含まれていません.でも便利そうだから色んな実装が同じものを提供しています.ただ,実装依存だからそれぞれ微妙に違っていて,ポータブルにするのは地味に面倒です.

で,それをまとめたのが trivial-gray-streamsです.quicklispで使えます.市民,幸福ですね?

そのREADMEを訳したっぽいですよ.参考サイトはこちら.
http://common-lisp.net/project/trivial-gray-streams/

trivial-gray-streams

Gray streams は ANSI CL に含めるために David N.によって,『ユーザ定義ストリーム』 STREAM-DEFINITION-BY-USER (http://www.nhplace.com/kent/CL/Issues/stream-definition-by-user.html)の中で提案されました.

この提案は ANSI CL には取り入れられませんでしたが,多くの有名なCL実装がこれを実装しています.

このシステム(trivial-gray-streams)は,gray streamsのための,ごく薄い互換性のレイヤーを提供します.

これは完全なパッケージというには*とても*小さいものかもしれません.でも,もう十分なくらいたくさんのプロジェクトに同じコードをコピペしたんで,僕はこれをまとめて取り出して,もう*二度と*触りたくないです!

つかいかた

  • gray streamのシンボルを取得するのに,貴方が使わなくてはならない実装依存のパッケージの代わりに,TRIVIAL-GRAY-STREAMS パッケージを使うようにしてください.
  • STREAM-READ-SEQUENCE と STREAM-WRITE-SEQUENCE について言っておくと,2つの必須引数とキーワード引数を使うことができます.なのでどちらのメソッドを定義するときも,ラムダリストは次のようになります
(stream sequence start end &key)

かくちょう

Generic function STREAM-READ-SEQUENCE (stream sequence start end &key)
Generic function STREAM-WRITE-SEQUENCE (stream sequence start end &key)

上を見てください

Generic function STREAM-FILE-POSITION (stream) => file position
Generic function (SETF STREAM-FILE-POSITION) (position-spec stream) => successp

これらは ABCL, ACL, Lispworks, CCL, CLISP, SBCL, そして MOCL でのみ呼ぶことができます.

中身について補足(訳者)

このシステムは,各実装が提供している gray-streams 関連パッケージから,以下に挙げるクラス・関数をインポートし,たいていはそのままエクスポートしているだけっぽい.

だから,各実装の gray-streams 関連ドキュメントを読んだあと,実際に使う時は実装依存のパッケージではなく,このシステムを使うようにすればいいっぽい.

ただ,このパッケージからシンボルを取ってきてねといっても,どんなシンボルがエクスポートされてるかが分かんないとどうにもなんないですね.はい,次がそのシンボルでーす.実際に使う場合はちゃんとpackageの定義も読みませう.

システムが提供しているクラス
  • fundamental-stream
  • fundamental-input-stream
  • fundamental-output-stream
  • fundamental-character-stream
  • fundamental-binary-stream
  • fundamental-character-input-stream
  • fundamental-character-output-stream
  • fundamental-binary-input-stream
  • fundamental-binary-output-stream
システムが提供している関数
  • stream-read-char
  • stream-unread-char
  • stream-read-char-no-hang
  • stream-peek-char
  • stream-listen
  • stream-read-line
  • stream-clear-input
  • stream-write-char
  • stream-line-column
  • stream-start-line-p
  • stream-write-string
  • stream-terpri
  • stream-fresh-line
  • stream-finish-output
  • stream-force-output
  • stream-clear-output
  • stream-advance-to-column
  • stream-read-byte
  • stream-write-byte
実装依存のパッケージにはない, trivial-gray-streams 独自の総称関数
  • stream-read-sequence
  • stream-write-sequence
  • stream-file-position