Drop multiple return values and have proper tuples. Replace UTF-8 by convention with real UTF-8. Make pointers non-nullable and replace null with sum types (compiler can still use null). Remove switches, and provide pattern matching instead. Remove lots of more hard-wired stuff.
Conversation
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)
1
1
(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
You would need to assume that absolutely any call could fail from allocation failure and catch it absolutely everywhere, but the compiler isn't going to help you with that. Losing threads doesn't count as actually handling it either esp. when not able to deal with many cases.
also, libunwind currently allocates, and doesn't even handle malloc returning NULL! I added _LIBUNWIND_NO_HEAP upstream, but that makes unwinding much slower, so it's off by default.
1



