|
|
第26回 例外の「中断・終了処理」としての側面例外の発生は,Haskellを含む多く言語にとって,それまで行っていた計算の強制的な中断・終了を意味します。例外の発生による計算の中断は,並行処理,中でもSoftware Transactional Memory(STM)ではどのような意味を持つでしょうか? また,例外の発生による計算の中断をより安全に行うにはどうすればよいでしょうか? 今回は,例外の「中断・終了処理」としての側面を説明します。
例外の発生による処理の中断並行処理での例外の発生について説明する前に,逐次的な処理での例外の発生について復習しておきましょう。 前回は,「例外の発生」と「そこからの回復」の二つを一組にして説明しました。しかし、これらは必ず一対一に対応するとは限りません。他の多くの言語と同様に,Haskellの例外は処理の終了を意味するため,例外が捕捉される際に行われていたすべての処理が終了することになるからです。 この事実は,「例外を発生させる」ことが,あらゆる計算処理を中断・終了させる手段になりうることを示しています。例えば,無限ループを以下のように例外によって終了させることができます。 module AsyncException where
import Control.Monad
import Data.IORef
foreverTest = do
ref <- newIORef 0
forever $ do
var <- readIORef ref
if var <= 10
then do
print var
modifyIORef ref (+1)
else error "varaible is greater than 10."*AsyncException> foreverTest 0 1 2 3 4 5 6 7 8 9 10 *** Exception: varaible is greater than 10. foreverは無限ループを記述するための関数で,Control.Monadモジュールで定義されています。 forever :: (Monad m) => m a -> m b forever a = a >> forever a Prelude Control.Monad> forever $ print "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." "inifinite loop." 〜 略 〜 foreverTestが例外を発生させなければ,「前の値に1を足した値を出力する」処理を以下のように延々と続けることになります。 foreverTest' = do
ref <- newIORef 0
forever $ do
var <- readIORef ref
print var
modifyIORef ref (+1)*AsyncException> foreverTest' 0 1 2 3 4 5 6 7 8 9 10 11 〜 略 〜 47264 47265 47266 47267 47268 47269 47270 〜 略 〜 もっとも,例外をgotoやcall/ccのような大域脱出の手段として使うのは望ましいことではありません(参考リンク1,参考リンク2)。たいていの場合,例外によって大域脱出を無理矢理行わなくても,再帰やmapM*などの高階関数を使うことで,大域脱出を使わないようにコードを書き直せます。一方で,例外の発生を「あらゆる処理を中断するための手段」とみなすことは,並行処理の環境においては重要な意味を持ってきます。 |