How should a function report an error?

Functions can fail. I have functions that try to find a steady state of a DAE system, and that can fail. What is better:

  • to raise an exception
  • to return nothing

While this error can be fatal, it does not have to be, trying again with different parameters might help.

Extra question:
Should a linter give a warning if a function returns a value or nothing?

On this question, I really appreciate the design of Roots.jl. Its high-level API is opinionated and throws on failure, while its lower-level API gives advanced users more control over how to handle non-convergence.

I think that is a very good pattern. A high-level function should usually be safe and explicit by default. But lower-level interfaces can expose alternative failure-handling strategies for users who really need them.

If you do choose to throw, I would also recommend using a custom exception type. That way, downstream code can still recover cleanly with try/catch when appropriate, without having to inspect error strings or catch unrelated exceptions.

So for me, it depends on the API’s abstraction level.

For me, nothing would be too uninformative.

Similar to Roots.jl, I also prefer for internal functions to maybe also just return the error or in some way information what/why things failed – and the high level parts to throw those.

I could not find any lower level API in the documentation of Roots.jl. Which functions are you talking about? How do they report a failure?

Unlike find_zero, solve will return NaN on non-convergence.

And find_zero will throw Roots.ConvergenceFailed.

I think I will return the tuple <result>, error. I cannot use NaN because my result is not a number. Then the caller can decide to print a warning in case of an error, or to retry with different parameters.