"I happen to agree on type first, disagree on single return (and it's a shame Zig doesn't seem to be on the radar especially because of this)"
Can you clarify what you mean by this?
"I happen to agree on type first, disagree on single return (and it's a shame Zig doesn't seem to be on the radar especially because of this)"
Can you clarify what you mean by this?
https://ziglang.org/documentation/master/#Errors https://ziglang.org/documentation/master/#Optionals
Yes, you can do that, but it's always going to be far more cumbersome than native multi-value return would be. Even in the simplest case (stack allocation in the caller), you have to define a new struct that has no other purpose, in a place visible to both caller and callee. You also have to refer to the status and value as struct members rather than simple variables. That's already worse than using a return value for the status and a reference parameter for the value.
I guess you could say that multi-value return is just syntactic sugar for more reference parameters. That's mostly true at the source level, but at the object level not quite. Using a reference parameter requires an extra stack operation (or register allocation) to push the parameter's address, in addition to whatever was done to allocate the parameter itself. Then the callee has to access it via pointer dereferences. With true multi-value return neither is necessary. The exact details are left as an easy exercise for any reader who knows how function calls work at that level. (BTW yes, interprocedural optimization can make that overhead go away, but a lot of code is written in C specifically to avoid relying on super-smart compilers and runtimes.)
Multi-value return isn't a huge deal, though. I appreciate having it in Python, but in almost thirty years I rarely missed it in C. While I wouldn't necessarily oppose its addition, I think it's more in keeping with C's general spirit of simplicity to leave it out.
A interface devised just to unwind information from a two member struct is beyond 'cumbersome'.
The nicest thing: you can throw in the exit method without the runtime falling over itself.
You can do a kind of RAII in D when you use structs instead of classes because these are stack allocated and have a suitable lifetime.
What non-object-oriented approach would you suggest instead?
That's not abuse, that's the most important pattern in C++ (RAII). You mustn't think of C++ "objects" as Java or C# or Smalltalk objects - they are not, they are deterministic resource managers before everything.
As long as people persist in calling them objects, and use all of the other object-oriented concepts/terminology such as classes and inheritance, people will expect them to be objects. That's not unreasonable. It's absurd to look down your nose at people who are taking you at your word.
These other uses are hacks. If you want to be able to attach something to a scope that's great, actually it's a wonderful idea, but just be honest about it. Make scopes a first-class concept, give things names that reflect their scope-oriented meaning and usage. Python's "with" is a step in the right direction; even though the implementation uses objects, they're objects that implement a specific interface (not just creation/destruction) and their usage is distinct. That separation allows scope-based semantics to evolve independently of object-lifetime semantics, which are already muddled by things like lambdas, futures, and coroutines. Tying them together might be an important pattern in C++, but it's also a mistake. Not the first, not the last. Making mistakes mandatory has always been the C++ way.
We don't want to attach resources to scopes, we want to attach them to object lifetimes. That's why defer/with/unwind-protect are not alternatives to RAII. The lifetime of an object I pushed to a vector is not attached to any lexical scope in the program text, it is attached to the dynamic extent during which the object is alive. While a scope guard always destroys its resource at the end of a block, RAII allows the resource lifetime to be shortened, by consuming it inside the block, or prolonged, by moving it somewhere with a dynamic extent that outlives the end of the block.
Here's an example where defer solves nothing: if I ask Zig to shrink an ArrayList of strings, it drops the strings on the ground and leaks the memory because Zig has no notion of the ArrayList owning its elements. You need to loop over the strings you are about to shrink over and call their destructors, which is literally the hard part, since the actual shrink method just assigns to the length field. The lack of destructors (Zig has no generic notion of a destructor) here impedes generic code since what you do for strings is different than what you do for ints.
RAII guards are real objects, they contain real data (drop flags), and they make code safer and more generic. If you don't like RAII, show comparable solutions (which scope guards are not), don't just call it a hack and adduce philosophical notions of how OOP should work.
Is that the royal "we"? Because for people who aren't you, it's only true some of the time. Sure, true resource acquisition/release is tied to object lifetimes. That's almost a tautology. But that doesn't work e.g. for lock pseudo-objects, which very much are expected and meant to be associated with a scope. It just happens to work out because the object and scope lifetimes are usually the same, but it's still a semantic muddle and it does break for things like lambdas and coroutines.
> if I ask Zig to shrink an ArrayList of strings
That's a silly and irrelevant example, having more to do with ownership rules (which C++ makes a very unique mess of) more than scopes vs. objects. Any Zig code anywhere that shrinks a list of strings had better handle freeing its (now non-) members. No, defer doesn't cover that case. Yes, destructors would, but this isn't an OO language. The obvious solution (same as in C) is to define a resize_string_list function. Again, what can you suggest in a non-OO language that's better?
> The lack of RAII here impedes generic code
You really don't want to get into a discussion about C++ and generics. Trust me on that. Yes, you need to do different things for strings and ints, but there are many ways besides C++'s unique interpretation of RAII (e.g. type introspection) to handle that.
> If you don't like RAII, show comparable solution
Done. Your turn. If you want to be constructive instead of just doctrinaire, tell us what you'd do without OO to address these situations better than existing solutions.
P.S. Also, what's with all the nonsense-word accounts in this thread taking offense at things said to jcelerier and responding with exactly the same points in exactly the same tone?