Description
This issue is to discuss approaches to handle runtime errors and exceptions in stdlib. I have in mind scenarios such as
- An iterative solver algorithm fails to converge
- A negative number is passed to a routine that requires a nonnegative one
- Not enough memory for an array allocation
These are conditions which cannot be identified at compile time, but if left alone will either cause user code to crash or be erroneous. Currently in stdlib, we have check
, which can print error messages and optionally terminate execution. I think it is not sufficient for general-purpose runtime checking. To me, there are a few major considerations when thinking about runtime checking
-
Users must be given the opportunity to recover from the error if at all possible. This is especially important for library code, which may be difficult to debug depending on the installation/distribution. It is also generally rude for library code to kill execution without giving the user any say in the matter.
-
It should be possible to recover from runtime errors without sacrificing purity. If a routine is manifestly pure, then it should not have to sacrifice the
pure
attribute just to have some error checking. If I write a factorial function, making itpure
and handling negative arguments should not have to be an either/or proposition. -
Checking and handling errors should not be unduly burdensome to users. If a function call requires one line, and handling its possible error conditions requires ten, users will simply not bother with error checking.
This list of criteria is not exhaustive, but they are the three that are most important to me. That stated, here are the runtime checking approaches I am most familiar with and how they stack up:
-
Return error code and message as optional out-params.
- Does not play nicely with pure functions (pure subroutines OK, though)
- Intrinsic functions do not do this (but statements do, e.g.,
iostat
,iomsg
) - Passes the buck to the user to interpret and handle the error code/message
-
Die with
error stop
- As of f2018, can be used in pure functions
- Kind of rude for library code to kill execution without chance of recovery
-
Return a special value
- This is what intrinsics usually do, e.g.,
index
returning -1 - Some algorithms may not have a natural "obviously wrong" value (maybe NaN?)
- Works with pure functions/subroutines
- This is what intrinsics usually do, e.g.,
-
Raise an exception
- Maybe in 2045?
Of these, my preference is strongly toward approach 3 whenever possible.