Hello. Thanks for your work on the Mojo language.
Right now, Mojo does not merge exceptions by default when multiple exceptions are raised, especially during cleanup failures. May it change in a future version of Mojo?
Python is designed to merge exceptions by default when multiple exceptions are raised. For example, in each of the 3 below codes, the error message contains the lines:
Exception: regular errorDuring handling of the above exception, another exception occurred:Exception: cleanup failure
First code:
try:
raise Exception("regular error")
finally:
raise Exception("cleanup failure")
Second code:
class Context:
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
raise Exception("cleanup failure")
with Context():
raise Exception("regular error")
Third code:
class AsyncContext:
async def __aenter__(self):
return self
async def __aexit__(self, exc_type, exc_value, traceback):
raise Exception("cleanup failure")
async def main():
async with AsyncContext():
raise Exception("regular error")
import asyncio
asyncio.run(main())
In Mojo, syntactic sugar over merging exceptions with finally seems challenging because the user can create its own error types which may not support merging with other errors. But merging errors is easier with context managers.
In Errors, error handling, and context managers, I read that Mojo allows to write several __exit__() methods. When there is an error as an argument, __exit__() can return True, return False or raise a new error. Raising a new error gives the potential to merge errors if a cleanup failure occurs.
But I saw that FileHandle does not have an __exit__ method and that FileHandle.__deinit__ discards the potential error raised by self.close(). The current design is similar to C++ and Rust which silent errors in destructors in their standard libraries.
This FileHandle design decision seems to come from the current Mojo Error structure which does not seem to be designed for multiple errors, although we can tinker with the error message to include multiple suberror messages. But why is the current Error structure like this?
In Mojo vision, I read that Any deviation from Python requires a strong, mission-driven justification. Also, in the current Mojo design, the presence of @explicit_destroy suggests that Mojo’s resource cleanup aims to be better than C++ and Rust.
What is the rational about the current error handling design? May it change in a future version of Mojo?
Remark: This message focuses on the fallible cleanups, but merging multiple errors has other use cases, for example:
- spawning several parallel fallible tasks, waiting for their completion and collect all errors and
- parsing multiple parts from an input and collect all parsing errors.