SIGINT等のシグナルの扱い方です。
tl;dr
System.Posix.Signalsを使います
2019/1/25 追記
WindowsでCtrl-Cを制御する方法については @kakkun61 氏の記事を参照してください
Haskell で Ctrl-C を制御する(Windows) - 趣味はデバッグ……
Ctrl-Cが押されたらクロージングの処理を伴って終了するプログラムを書いてみます。
import Control.Concurrent
import System.Posix.Signals
main :: IO ()
main = do
tid <- myThreadId
let handler = do
putStrLn "goodbye!"
killThread tid
installHandler keyboardSignal (Catch handler) Nothing
let loop n = do
putStr $ show n ++ ", "
threadDelay 1000000
loop (n+1)
loop 0
実行してみましょう
$ runhaskell Main.hs
0, 1, 2, 3, 4, 5, ^Cgoodbye!
Main.hs: thread killed
$
ちゃんとgoodbyeと出力されて終了しました!killThreadは例外を伴って終了するのでMain.hs: thread killedというメッセージが出てしまっています。もし気になるなら例外を握りつぶすかMVarを使って終了を監視する仕組みを作るといいでしょう。
肝心のSignalを制御する関数はinstallHandler :: Signal -> Handler -> Maybe SignalSet -> IO Handlerです。Signelには制御するSignal、HandlerにはSignalが投げられた時の処理、Maybe SignalSetには処理時にブロックする他のSignalを指定します。
Signalの値は予め用意されていて
| Function | Signal |
|---|---|
| keyboardSignal | SIGINT |
| keyboardStop | SIGTSTP |
| keyboardTermination | SIGQUIT |
| openEndedPipe | SIGPIPE |
と言った感じです。
HandlerはDefaultにすれば標準の動作を、Ignoreにすれば何もしなくなり、Catch (IO ())で処理を記述すればSignalが投げられた時の処理を記述することができます。上の例ではhandlerという関数にメッセージの表示とメインスレッドの停止の処理を書いてHandlerとして渡していました。
簡単に解説しましたがまだまだ色々な使い方ができるので是非一度System.Posix.Signalsを読んでみてください(^^)