it sounds like you basically want to use Reason with Core (since Core makes a point of avoiding exceptions, and I don't think they use classes anywhere)
Conversation
(typeclasses, well, there's modular implicits, but I don't know when or if they'll be upstream. it's taking a while, but multicore is also taking a while in spite of being actively worked on, and that's just how OCaml is, I guess...)
3
I am curious how these languages "without exceptions" (of any sort) manage failures of the language's abstract machine (eg, allocation failure and other run-time errors). Go has an exception (panic) and a slightly devious way to recover it.
1
Go doesn't truly handle allocation failures. Also, in a high-level language, I want thread-local heaps and automatic resource management not only automatic memory management. I don't believe in invisible control flow with hidden and rarely if ever tested code paths everywhere.
1
1
For a low-level language with meaningful out-of-memory handling, I think Rust's approach to error handling is fine. The issues I have with Rust are really with the standard library. Disabling it via the attribute and using only the core standard library avoids unwinding, etc.
1
I think that's the main issue with it: for many truly compelling use cases, where you actually want a low-level language, a lot of the standard library isn't suitable. It's nice there's a core standard library for use in a kernel, C ABI library, etc. but there should be way more.
1
1
So, for example, with Vec<T>, a method like vec.push(value) just has to return Option<T> with #[must_use] where Some(T) means that memory allocation failed, and it hands you back ownership of the element you passed into it. It works well and composes well. It's not that bad.
1
Unwinding on allocation failure is a decision not to handle it. The same thing applies to most errors. It isn't actually considered during development and those code paths aren't visible in the code or tested in practice. You need the ability to inject allocation failures too.
1
I don't think most code, even low-level code, actually wants to handle it in much depth, other than running a generic handler to drop cached memory, try again, and then exit cleanly. When you do want to handle it, hidden unwinding on allocation failure really isn't good enough.
2
1
"running a generic handler to drop cached memory, try again, and then exit cleanly" is something that Rust's now-defunct condition system, and not much else, would have handled perfectly
1
en.cppreference.com/w/cpp/memory/n is a decent approach. An implementation as an example: github.com/GrapheneOS/har. It doesn't work well in C++ because it's only for new and people don't use it whenever they call malloc, mmap, etc. but it's an approach that can work well for the common case.
I think that's what most applications actually need, and they can and should be using a multi-process architecture for reliability and security too. In libraries, a kernel, or some applications, you do want to handle it beyond this though, and unwinding really isn't good enough.
that's the obvious way to do it without direct language support, but it doesn't seem like it would compose well--would you call that in a library?
1
No, but it could have been designed to work like atexit, instead of a single handler like this. This specific API is flawed, but the idea behind it and the general approach is a good way to handle the needs of most applications. It's weird there isn't a widely adopted approach.
1
Show replies


