[ghc-steering-committee] Proposal #631: Set program exit code by main return type, recommendation: accept something

Arnaud Spiwack arnaud.spiwack at tweag.io
Thu Feb 29 10:28:37 UTC 2024

Dear all,

Shea Levy proposes to do something with the values returned by `main`
https://github.com/ghc-proposals/ghc-proposals/pull/631 .

The problem is that `main` is allowed to be of type `IO A` for any `A`. And
GHC will simply drop the value returned by `main`. Shea contends that it's
surprising. I agree that dropping a value without the compiler being
explicitly instructed to is surprising. But Shea says that when `A` is
`ExitCode` this is even more surprising. Namely `main :: IO ExitCode; main
= return $ Failure 1` actually terminates with exit code 0. And I doubt
that it's what anybody expects when reading the code.

The proposal is simple, but I have a lot of comments on it. Sorry about

Now, this sort of proposal is tricky. When the current behaviour is
confusing, we want to change the default. But putting the new default
behind an extension doesn't really solve the fact that there's a trap. The
extension is, therefore, unlikely to be well tested before it becomes part
of the next language edition.

Shea's main proposition doesn't actually use an extension though. He adds a
type class `ExitStatus`, and if `ExistStatus A`, then `main :: IO A` uses
the instance to determine the exit code based on the return value.

The only change to the current behaviour is that `main :: IO ExitCode`
instead of always terminating with exit code 0 when returning now
terminates with the expected error code. The argument for not putting this
behind an extension is that virtually anybody affected by the change will
actually have the behaviour they were expecting. But maybe the argument
isn't strong enough (the changes may be more “interesting” if some library
exports some `ExistStatus` instance).

This design of this proposal is inspired by Rust's design. I've asked our
Rust team, and they certainly seem to have internalised the idea of
returning an exit code. It really seems a pretty natural feature to have.
So I'm rather in favour of some flavour of the type class implementation.
Though have a look at the alternatives, where you'll find other approaches
such as restricting the type of `main` to unsurprising types.

One caveat with respect to the main proposal: it is proposed that when no
`ExistStatus A` is found, then we drop the returned value like today. I
don't know that it's quite easy to implement this behaviour. But it can be
recovered by a catch-all overlapping instance, so maybe it's a better way
to specify the desired behaviour.

Arnaud Spiwack
Director, Research at https://moduscreate.com and https://tweag.io.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-steering-committee/attachments/20240229/a3c7702c/attachment.html>

More information about the ghc-steering-committee mailing list