abst@ype fildes = int
抽象型に衝突を引き起こしそうにない名前をつけるのはしばしば良い習慣です。
そしてその名前に短かい別名を導入するのです。
例えば、次の宣言はそのような習慣を示しています:
abst@ype fildes_t0ype = int stadef fildes: t@ype = fildes_t0ype私の命名規約では、種 t@ype の型、すなわちサイズが不明な型を表わすのに特殊な識別子 [t0ype] を使います。 stadef 宣言は次のようにも置き換え可能であることに注意してください:
typedef fildes = fildes_t0ypeさて、ファイルコピー関数を [fcopy1] と名付けて、次のインターフェイスを与えましょう:
fun fcopy1 (src: fildes, dst: fildes): void
[fcopy1] をどのように実装すべきでしょうか?
差し当たり、幾分抽象的な作法でこの問に答えることにしましょう。
[src] から文字を読み出し、[dst] へ文字を書き込めるべきであることは明確です。 そこで、次の2つの関数が使えると仮定しましょう:
fun readch (src: fildes): char fun writech (src: fildes, c: char): voidまたもう一つあります: 与えられたファイルから全ての文字を読み終わったかどうか分からなければなりません。 簡単な方法は、[readch] がファイルの最後に達したことを示す特別な値を返すようにすることです。 この目的のために、[readch] のインターフェイスを次のように修正します:
fun readch (src: fildes): int
非負の整数は有効な文字を、負の整数 (例えば -1) は
([src] の最後に達したことを示す) 特別な値とします。
これで [fcopy1] を次のようにたやすく実装することができます:
implement fcopy1 (src, dst) = let val c = readch (src) in // if c >= 0 then (writech (dst, c); fcopy1 (src, dst)) // end of [if] // end (* end of [fcopy1] *)ここで当然次のような疑問がわくでしょう: 関数 [readch] と [writech] はどうすれば実装できるでしょうか? それらはシステムコール [read] と [write] に基づいて実装できます。 [fcopy1] に基づいたファイルコピーの実行可能な実装は fcopy1.dats から入手できます。 このとき [readch] と [writech] はC言語で直接実装されています。
もちろん、上記のファイルコピー実装には多くの批判があるでしょう。 例えば、エラー処理のようなものを全くサポートしないため、ほとんど役に立ちません。 これらの課題については次の試みで解決しようと思います。 けれども、この実装において建設的だったのは関数 [readch] と [writech] を導入したことです。 これらの関数はシステムコール [read] と [write] を直接使うことを防ぐレイヤーになっています。 このプログラミングスタイルを私は何度も繰り返し強く提言してきました。 私にとって非常に残念なことですが、たとえシステムプログラミングに関する著名な本 (例: APUE ) がこの有用なプログラミングスタイルを推奨していなかったとしてもです!
absvtype buffer_vtype = ptr vtypedef buffer = buffer_vtype[buffer_vtype] が viewtype、つまり線形型として導入されています。 そして次はそれぞれバッファを生成/削除するための関数です:
fun buffer_create (m: size_t): buffer fun buffer_destroy (buf: buffer): voidまた、バッファがデータを含むか否か検査する必要があります。 この目的のために次の関数を導入しましょう:
fun buffer_isnot_empty (buf: !buffer): bool
さらに、[readbuf] と [writebuf]
をそれぞれ複数文字を読み書きする関数として作り、それらに次の型を割り当てましょう:
fun readbuf (src: fildes, buf: !buffer): void fun writebuf (dst: fildes, buf: !buffer): void[fcopy2] には [fcopy1] と同じインターフェイスを与えることにしましょう。 次のコードは [readbuf] と [writebuf] を用いた [fcopy2] の率直な実装です:
implement fcopy2 (src, dst) = let // fun loop ( src: fildes, dst: fildes, buf: !buffer ) : void = let val () = readbuf (src, buf) val isnot = buffer_isnot_empty (buf) in if isnot then let val () = writebuf (dst, buf) in loop (src, dst, buf) end else () // end of [if] // end // end of [loop] // val buf = buffer_create (i2sz(BUFSZ)) val () = loop (src, dst, buf) val () = buffer_destroy (buf) // in // nothing end (* end of [fcopy2] *)[BUFSZ] はコンパイル時定数の整数、[i2sz] は型 [int] の整数から [size_t] の値へのキャスト関数であることに注意してください。 [fcopy2] を用いたファイルコピーの実行可能な実装は fcopy2.dats から入手できます。 このとき [readbuf] と [writebuf] はC言語で直接実装されています。
[read] もしくは [write] の呼び出しは様々な要因で失敗する可能性があることは明白です。 もしそのような失敗が起きたら、おそらくファイルコピーを停止してエラー報告をすべきでしょう。 次のような別のファイルコピー関数 [fcopy3] を導入してみましょう:
fun fcopy3 (src: fildes, dst: fildes, nerr: &int): void
[fcopy3] の3番目の引数 [nerr] は参照渡し (call-by-reference) の整数です。
別の言い方をすると、[fcopy3] の 3番目の引数として渡されるのは、左辺値のアドレスです。
もしファイルコピーの最中に [read] もしくは [write]
に起因するエラーが発生したら、[nerr] に保管されている整数値を増やすべきです。
これを実現するために、[readbuf] と [writebuf] の方を次のように変更します:
fun readbuf (src: fildes, buf: !buffer, nerr: &int): void fun writebuf (dst: fildes, buf: !buffer, nerr: &int): void関数 [readbuf] が [read] を呼び出すと; もしこの呼び出しがエラーを報告したら、[readbuf] はその3番目の引数に保管されている整数値を増やします。 関数 [writebuf] も [write] について同じ動作をします。 次のコードは [fcopy2] と同じように [fcopy3] を実装しています:
implement fcopy3 (src, dst, nerr) = let // fun loop ( src: fildes, dst: fildes, buf: !buffer, nerr: &int ) : void = let val nerr0 = nerr val () = readbuf (src, buf, nerr) val isnot = buffer_isnot_empty (buf) in if isnot then let val () = writebuf (dst, buf, nerr) in if nerr = nerr0 then loop (src, dst, buf, nerr) else ((*error*)) end else () // end of [if] // end // end of [loop] // val buf = buffer_create (i2sz(BUFSZ)) val ((*void*)) = loop (src, dst, buf, nerr) val ((*void*)) = buffer_destroy (buf) // in // nothing end (* end of [fcopy3] *)[read] もしくは [write] に起因するエラーが報告されると loop 関数が終了することに注意してください。 [fcopy3] を用いたファイルコピーの実行可能な実装は fcopy3.dats から入手できます。 このとき [readbuf] と [writebuf] はC言語で直接実装されています。