PR

Errno以外のエラー・コードやシグナルなどを使う

 今回はerrnoに格納されているエラー・コードをI/Oエラーに変換する方法を解説しました。ただ,実際のプログラミングではErrno以外のエラー・コードやシグナルを扱うこともあるでしょう。このうち,OSのAPIを使うのに必要不可欠な関数は,それぞれのOSに対応したパッケージのモジュールで定義されています。Windowsのエラー・コードを扱う関数はWin32パッケージのSystem.Win32.Typesモジュール,POSIXシグナルを扱う関数は,unixパッケージのSystem.Posix.Signalsモジュールでそれぞれ定義されています。

 System.Win32.Typesのエラー・コードに関係する部分は以下の通りです。

Prelude System.Win32.Types> :browse
~ 略 ~
type DWORD = GHC.Word.Word32
type ErrCode = DWORD
~ 略 ~
c_maperrno :: IO ()
~ 略 ~
errorWin :: String -> IO a
failIf :: (a -> Bool) -> String -> IO a -> IO a
failIfFalse_ :: String -> IO Bool -> IO ()
failIfNull :: String -> IO (GHC.Ptr.Ptr a) -> IO (GHC.Ptr.Ptr a)
failIfZero :: (Num a) => String -> IO a -> IO a
failIf_ :: (a -> Bool) -> String -> IO a -> IO ()
failUnlessSuccess :: String -> IO ErrCode -> IO ()
failUnlessSuccessOr :: ErrCode -> String -> IO ErrCode -> IO Bool
failWith :: String -> ErrCode -> IO a
getErrorMessage :: DWORD -> IO LPWSTR
getLastError :: IO ErrCode
~ 略 ~

 ErrCodeがForeign.C.ErrorモジュールのErrno,errorWinがthrowErrno,failIfがthrowErrnoIfに相当します。このように,Windowsのエラー・コードを扱う関数は,独自の名前を用いている点を除けば,Foreign.C.Errorモジュールに似たものになっています。

 一方,System.Posix.Signalsで扱うシグナルは,数値とそれに対応するメッセージだけを提供しているエラー・コードとは,少し性格が異なります。シグナルが発生した場合に行うべき処理を記述する仕組みを持つ本格的な例外機構になっています(参考リンク1参考リンク2)。このようなシグナルを直接I/Oエラーに結び付けることはできないため,System.Posix.Signalsではシグナルを扱うための独自の関数を提供しています。

Prelude System.Posix.Signals> :browse
data Handler = Default | Ignore | Catch (IO ()) | CatchOnce (IO ())
type Signal = Foreign.C.Types.CInt
newtype SignalSet
  = System.Posix.Signals.SignalSet (GHC.ForeignPtr.ForeignPtr
                                      System.Posix.Internals.CSigset)
addSignal :: Signal -> SignalSet -> SignalSet
awaitSignal :: Maybe SignalSet -> IO ()
backgroundRead :: Signal
backgroundWrite :: Signal
badSystemCall :: Signal
blockSignals :: SignalSet -> IO ()
~ 略 ~
getSignalMask :: IO SignalSet
illegalInstruction :: Signal
inSignalSet :: Signal -> SignalSet -> Bool
installHandler ::
  Signal -> Handler -> Maybe SignalSet -> IO Handler
~ 略 ~
queryStoppedChildFlag :: IO Bool
raiseSignal :: Signal -> IO ()
~ 略 ~

 これらの独自関数にはHaskellの例外処理との互換性はありません。Windowsと互換性がある処理や高レベルの処理を作成する場合には,第5回で紹介したSystem.Processのように,シグナルを実装の内側に隠して高いレベルでI/OエラーやHaskellの例外に対応させるとよいでしょう(参考リンク)。

 最後に,ライブラリがerrnoでもWindowsのエラー・コードでもシグナルでもない独自のエラー処理の仕組みを提供していた場合にはどうすればよいでしょうか?

 この場合,ライブラリのエラー処理がForeign.C.ErrorとSystem.Posix.Signalsモジュールのどちらに似ているかを考え,似ているほうと同様の方針でプログラミングしていけばよいでしょう。エラーに対応させるべき例外がHaskellのI/Oエラーに含まれない場合には,第25回で説明したように新しい例外型を作成して使用することもできます。


著者紹介 shelarcy

 「Real World Haskell」がようやく出版されました。関数型プログラミングの学習という立場からではなく,実際にプログラミングを行うための道具としてのHaskellについて書いてある本が出たことは,素直に喜ばしいことだと思います。「このあたりにもっと踏み込んでほしい」と思うところはいくつかありますが,すでに670ページもある本書にこれ以上の内容を求めるのは酷なことかもしれません。本連載の過去の回には,Real World Haskellよりも詳しく説明した話題もいくつかあります。この本に足りないところは本連載で補完していければと思います。

 他にも,GHCで.NETを利用するための新しいライブラリであるSalsaが公開されたり,F#がVisual Studio 2010に含まれるという話題も出ています。いよいよ関数型プログラミングが実用になる時代が近づいてきたのかもしれませんね(参考リンク1参考リンク2)。