Rename fatalError()

I'd like to propose introducing a new name for fatalError(). I am not proposing removing fatalError() as this would be a major breaking change. Perhaps we can take the same approach of SE-0214 where we introduced a new name for the type in 4.2 then in swift 5 deprecate the old name indefinably.

My major objection to the current name is that it contains the word "Error". I believe this is leading people to treat it as a way to throw unhanded exceptions (which swift does not have). I do not think this is a good path for swift. If we were to introduce an extensible way to fatalError() then at some point people are going to ask for swift to be able to handle those fatal errors and at this point we are back to just having plain Java like Exceptions.

I think we can just drop the Error from the name and still keep the gist of what the func does. This would also honor those whose mental muscle is to type fatal when reaching for fatalError.

public func fatal(
  _ message: @autoclosure () -> String = String(),
  file: StaticString = #file, line: UInt = #line
) -> Never {
  _assertionFailure("Fatal Failure", message(), file: file, line: line,
    flags: _fatalErrorFlags())
}

This could allow something like this in the future.

let result = some1 ?? some2 ?? fatal("Some really good message here")

We can keep fatalError around for a long long time as a deprecated function.

Additional Potential Features:

  • Do not provide default for the message so people would have to type fatal("") if they do not want to provide a message.
  • We could potentially provide more information in the logs when fatal happens since it is a new function.
2 Likes

It's been said before that the bar for considering a source-breaking change, which this is (or eventually would be with deprecation), would be that the existing facility is causing actual harm evidenced in real-world use. While it's unfortunate that fatalError isn't a recoverable Error [*], I'm not sure I see any evidence that people see fatalError and wonder if it throws.

[*] Although, to be fair, if Never were a real bottom type then it would be an Error.

If we were to need to rename this function, an idiomatic word that means "fatal" found in other programming languages is die. We could also consider maybe making this just an initializer for Never. But, again, I don't know that we really need to engage in this bikeshedding exercise right now.

1 Like

Besides the mentioned facts by @xwu I wonder if we're going to make any changes here if we could re-interpret fatalError into a runtime directive?

Bikeshedding code:

// before
fatalError("message")
// after
#error("message", at: .runtime)

I know it becomes more verbose but hey, that's just a small idea that I wanted to mention. It would at least gain some kind of a tooling hook.

The main idea is to move away from the error concept altogether for the main “crashing” function.

It is only harmful when it becomes the default fixit instead of force unwrap or if it overloaded to provide default reasons as I linked in the proposal above.

It actually does not matter to me what end up spelling it, as long as it doesn’t contain the word error in the name. I’d be in favor of die but I also like panic.

Since when is a depreciation a breaking chance? If somebody chooses to turn “warnings as errors”, well, that’s an opt in conscious decision by the company or individual. At some point we should be given the option to turn off certain warnings in swift like clang or dotnet offers.

Perhaps it is inevitable that fatalError could not be sunset; should that stop us from providing a better name in a new function?

1 Like

die would be a good name, and definitely has brevity on its side. Something with failure to match assertionFailure and preconditionFailure might work as well.

2 Likes

How about generalFailure or unexpectedFailure?

1 Like

we could convine this idea and @DevAndArtist
into something like:

runtimeFailure()

I don’t think anybody would think that the actual runtime failed. Most people refer to crashing as runtime failures.

2 Likes

trap()

2 Likes

The suggestions in this thread seem to want to rename fatalError rather than redesign it. I'd like to point out links that look at fatal outcomes through redesign:

I think fatalError is a term of art, although I agree die is to the point.

I'm a little bit concerned about die(...) -> Never, because it reads really strange to me.

1 Like

Perl is immortal

6 Likes

It reads pretty well, in my opinion! "Die returns never": when you die, you never return.

3 Likes

Well I get that if you turn the words around then it makes sense, but ask less experienced developers or non-natives. It's almost like never dies which is definitely not what we want, but I guess this would apply to any function name in that particular case.

Personally I'm not a fan of die as a term of art but would rather prefer something like trap or terminate.

+1 for getting rid of the confusing Error suffix.

Also, if we're revisiting fatalError, perhaps we should look at other functions in the "die" family too. Lots of people I've seen struggle to remember the difference between preconditionFailure, assertionFailure and fatalError, advanced users and beginners alike.

I’d prefer to leave well enough alone. The names are meaningful, well-understood, and in common usage already. There is no problem that needs to be addressed here.

8 Likes

fatalError isn't marked throws and the "fatal" portion of the name makes it pretty clear that this is an unrecoverable error. I think we should leave well enough alone.

9 Likes

I dislike the idea of this pitch. I feel like adding a new top level function that is identical to the current fatalError() be it die(), trap(), or terminate() doesn't add any value or clarity.

While I can understand the dislike of Error being part of the name, it is very clear from the name that fatalError() is going to die, since it is fatal. Error being part of the name makes it clear that it is to be used when something has gone wrong.

Also, I would argue that try!, fatalError(), and forced unwrapping nil, etc. are unhandled and unhandleable exceptions.

I do not agree with die() or terminate() for the simple reason that was discussed about the possibility of adding a fatalError handler - e.g. for server-side applications where the process could perform additional diagnostics and/or re-launch the process. For such case, I believe that the fatalError is both well-descriptive and what it actually does - it states that the process ran into a fatal error. Which is more precise than die(), terminate(), fatal() (fatal what?), etc.

2 Likes

I considered editing my post to clarify that I don't really care to change the name of fatalError. I was simply struck by the thought that, if we are going to change the name, trap would mean that method did what it said on the tin.

2 Likes

I always thought to trap meant to switch to a debugger or some other monitoring tool, but not explicitly terminate the program, so using it to replace fatalError() doesn't seem a good alternative to me. It also sounds more like an implementation detail than what the user actually wants to happen.

I quite like die() as it's very brief and clear and is obviously separate from other ...Error() cases that might have handlers in the future.