I was surprised to learn recently that the exitFailure
function doesn’t (directly) cause the program to exit. I wrote up an article about it:
I think I like the behavior of this particular function but I still wonder how this fits into my general wish for having a distinction of something like a “panic” and an exception. I understand that there’s rarely a case where you actually want to abort, no matter what, but I think it would still be nice if there was a useful distinction within Haskell between these two, it would avoid a lot of confusion (and having to read implementations).
I understand your thinking, but it has a smell of procedural execution (and strict evaluation). In a non-deterministic lazy language, “panic” or exitFailure
is a request in a (Monad
) sequence that should fire only when execution gets to try to reduce that term; and we the mere programmers can’t in general predict when that is relative to other sub-terms – that might also request “panic”/exitFailure
.
Here’s how I’d address the problem using Bluefin. I’d add an Exit
effect that encapsulates the ability to exit, and only allow it to be handled in IOE
. That means that you can define an App
effect that doesn’t allow general IOE
, but does allow Exit
, and then there’s no way whatsoever that the ExitCode
exception can be interrupted on its way up to runApp
.
runApp ::
e1 :> es =>
IOE e1 ->
(forall e. App e -> Eff (e :& es) r) ->
Eff es r
runApp = ...
runAppExit ::
e1 :> es =>
(forall e. Exit e -> (Eff e :& es) r) ->
Eff es r
runAppExit ...
main :: IO ()
main = runEff $ \io -> do
runApp io $ \app -> do
realMain app
- Prototype on GitHub
I have found exitImmediately helpful in the past when I for sure needed to kill the (multithreaded) program without having to orchestrate some signal from child threads to the main thread.
A proposal to GHC Meaningful main return type ExitCode is already accepted.
But it is not fully implemented: Implementation of extension MeaningfulMainReturn