プログラミング言語 Standard ML 入門 (問題の解答例)
15 入出力処理

15.5 入出力エラーの処理

問 15.9

以前作成したファイル変換関数に上記の入出力エラー処理を加え,
- lowerFile "foo.txt"; IO Error : openIn failed. No such file or directory : foo.txt val it = () : unit  
のようにエラーメッセージを表示するように改良せよ.

解答例  教科書本文にある通り、例外 IO.Io name:string, function:string, cause:exn を以下のようにhandleすればよい。 このエラー処理はlowerFile関数に以下のように追加できる。

   fun lowerFile inf outf =
       filterFile Char.toLower inf outf
       handle IO.Io {name,function,cause} =>
              (print ("IO Error : " ^ function ^ " faild. ");
               case cause of
                  OS.SysErr (s,e) => print (s ^ ": ")
                | _ => print (exnMessage cause ^ ": ");
               print  (name ^ "\n"))

しかし、エラーを処理しプログラムを継続する場合には、オープンして いるファイルのクローズ処理をするのが望ましい。 そこで、ファイルをオープンする関数lowerFileでエラー処理も 行うのがより適当である。 ただ、lowerFileは2つのファイルをオープンしており、クロー ズ処理が必要なオープン済みストリームはプログラムコードの位置に依存する。 この問題を系統的に処理する一つの方法は、ファイナライザ (finalizer)関数をエラー処理に渡す手法がある。 以下はその例である。

    exception Abort
    fun IOhandler ({name,function,cause}, finalizer)  =
        (print ("IO Error : " ^ function ^ " faild. ");
         case cause of
           OS.SysErr (s,e) => print (s ^ ": ")
         | _ => print (exnMessage cause ^ ": ");
         print  (name ^ "\n");
         finalizer();
         raise Abort)
   fun filterFile f inf outf =
          let val ins = openIn inf
                  handle IO.Io param =>
                         IOhandler (param,fn () => ())
              val outs = openOut outf
                  handle IO.Io param =>
                         IOhandler (param,fn () => closeIn ins)
          in
            (filterStream f ins outs
             handle IO.Io param =>
                    IOhandler
                      (param,
                      fn () => (TextIO.closeIn ins;
                                TextIO.closeOut outs));
              closeIn ins;
              closeOut outs)
          end
          handle Abort => ()
    fun lowerFile inf outf = filterFile Char.toLower inf outf

上記何れの場合も、SML#で以下のような実行結果となる。

   # lowerFile "foo.txt";
   val it = fn : string -> unit
   # lowerFile "foo.txt" "bar.txt";
   IO Error : openIn faild. No such file or directory: foo.txt
   val it = () : unit

なお、例から分かる通り、lowerFileは2つの引数を取る関数であり、 lowerFile "foo.txt"のみでは、実行されないため、エラーは発生しない。