Thank you to the author of the article for taking the time to try out both Odin and Zig.
I want to clarify my position as the comments you are commenting on are lacking a little nuance.
For the first one regarding OOM, Odin's allocators all support error values signalling things such as `Out_Of_Memory`, `Invalid_Pointer`, `Invalid_Argument`, and `Mode_Not_Implemented`. So if you you want to handle those error states, there is nothing preventing you from doing so in Odin. However, there are two aspects to my original argument: you should know the constraints of the platform you are on and plan accordingly as it is part of your problem; what happens in the case when you have completely ran out of memory (globally/system-wide)?
> My reaction to that is “640KB is enough for everything”, right?
I've worked on systems with 16KiB and that's just how much you had to deal with. Meaning we had to plan accordingly for what we needed so that we never ran out of memory. The program was heavily tested (both analyzed and empirically) so that we made sure that it ran within the constraint of 16 KiB. On such platforms, `malloc` was never called, and in fact banned.
> I have never had a program cause a system to run out of memory in real software (other than artificial stress tests).
In this comment, I say "program" and not _allocator_ here. I have many allocators that run out of memory _on purpose_. On desktop applications that I have worked on (I know this does not apply to all), if we did run out of memory, the better option is to just crash rather than try to recover. But of course some applications cannot do this, especially when dealing with third-party programs, such as databases (as you state). In those cases, the best you can do is empirically test how much is required and gracefully handle those cases when you do OOM (if possible). That's part of your problem, it's not an external thing. If you can control things, try to.
Minor note: Zig's allocators only return one possible error value `error.OutOfMemory`, whilst Odin has 4 possible error values meaning there is a finer granularity in Odin with its error value system for its allocators.
Onto the second comment you bring up regarding error value handling.
> ...My code, which is calling the service, need to be able to handle any / all of those
Yes. And you should handle them accordingly, and not just in a general "catch all", especially since these are wildly different kinds of failure states.
> ...Odin relies on the multiple return values system. We have seen how good that is with Go. In fact, one of the most common issues with Go is the issue with how much manual work it takes to do proper error handling.
Regarding error value propagation, I needed to clarify my position a little more in that article I was referring to an aggregate set of comments from a GitHub post and not a truly nuanced argument. Odin has the `or_return` operator which allows the user to easily propagate values in code. It is similar to Rust's `?` or Zig's `try` but complements Odin's multiple return values AND does not rely on having a concept of an "error value type".
Demo of `or_return` <https://github.com/odin-lang/Odin/blob/fd256002b3190076bb91e...> And check out Odin's [`core:math/big`](https://github.com/odin-lang/Odin/tree/master/core/math/big) which has big extensive use of the concept.
Even though I added this concept to Odin, I do believe that my general hypotheses are still correct regarding exception-like error value handling. The main points being:
* Error value propagation _ACROSS_ library bounds * _Degenerate states_ due to type erasure or automatic inference * Cultural lack of partial success states
The most important one is the degenerate state issue, where all values can generate to a single type. It appears that you and many others pretty much only want to know if there was an error value or not and then pass that up the stack, writing your code as if it was purely the happy path and then handling any error value. Contrasting with Zig, Zig has a built-in concept of an error value type, and all error values are either inferred `!`, have specific error set, or degenerate to `anyerror`. In practice from what I have seen of many Zig programmers, most people just don't handle error values and just pass them up the stack to a very high place and then pretty much handle any error state as they are all the same degenerate value: "error or not". This problem occurs in Go code too because everyone degenerates to the error interface in practice, therefore it's now equivalent to a fancy boolean.
For error values in Odin, you can use any data type you require for your problem, and even compose many together. It's very common to just use an `enum` or even a boolean, but sometimes you need something more complex or aggregate too. A good example of this is when you need to aggregate different possible error values from multiple different data types. The two common approaches are to make a mapping procedure to convert them to the specific `enum` OR to have a (discriminated) `union` of the `enum`s. This means Odin does not suffer from the degenerate state issue that Zig, Go, and many other languages do suffer from. This is because Odin doesn't have a built-in degenerate type which would be typically used for error values.
One issue with Zig's approach is due to its error value inference system, it's very difficult to know what possible errors values a procedure returns just from __reading__ the code (assuming `!T` or `anyerror!T` is used), you have to compile the code in order to determine that. This may be okay for a single developer, but if you are on a larger team you are now either relying on external documentation (which Zig can provide) or the compiler telling you this. With a specific (error) set this is not a problem because it's explicit to the reader.
Most of the time you are reading code, not writing code.
Odin's type system and feature set is rich enough that you can achieve everything you want _extremely cleanly_. You can many parallels between Odin and Zig: `errdefer` would be akin to `defer if err != nil {` in Odin, `try` would be akin to `or_return`, etc.
n.b. Someone recently wrote an article which is closer to my own position on error value handling (with some of the author's own flair and preferences) here: <http://tuukkapensala.com/files/software_does_not_contain_err...>
Thank you again for writing this article. And as I always say, I highly recommend people try out as many tools (which includes programming languages) as they can to find what are the best tools for your problems!
Regards,
Bill