exitFailure doesn't exit

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:

6 Likes

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
2 Likes

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.

1 Like

A proposal to GHC Meaningful main return type ExitCode is already accepted.
But it is not fully implemented: Implementation of extension MeaningfulMainReturn

1 Like