upvote
Was it ever about the language? Or was it really about, what kind of special interests can be tickled in the authors of greenfield, free software?

Programming language development has essentially ceased, no? Because now that you have a super powerful, ceaseless special interest personality that can be ordered to be specially interested in any problem. The actual problem - a dearth of things that special interest personalities will do for little to no money - has been solved.

reply
That doesn't solve the whole problem, though - as per the rule, "zero, one, infinity", you often want to return more than two values, and often none of them represents an error.
reply
But Rust doesn't care how many things you want to return?

    fn six_numbers() -> (u8, u8, u8, u8, u8, u8) {
        (10, 20, 30, 40, 50, 60)
    }

    fn in_an_array() -> [u8; 6] {
        [10, 20, 30, 40, 50, 60]
    }
The return type must be sized, because otherwise how can we put that anywhere? But of course your answer can just be to put it on the heap inside a Box...

    fn heap_numbers() -> Box<[u8]> {
        let mut slice = Box::new_uninit_slice(6);
        slice[0].write(10);
        slice[1].write(20);
        slice[2].write(30);
        slice[3].write(40);
        slice[4].write(50);
        slice[5].write(60);
        unsafe { slice.assume_init() }
    }
reply
I would not describe exceptions as return values. Speaking in a C context, exceptions are more like "longjmp" than "return".
reply
They're both ! Exception are definitely viewed as an alternate communication channel. And stack unwinding is one aspect of the communication. See https://nedbatchelder.com/text/exceptions-vs-status#h_valuab... (and many other)
reply
Arguably the better solution compared to these "algebraic sum types" (they go by countless different names) are set-theoretic union types, used by a few languages such as TypeScript.

They allow for types like "String or Integer". A function which returns a value of type "A" can automatically also be assigned to a variable of type "A or B", and a function which accepts a parameter of type "A or B" can automatically also be passed a variable of type "A". This is not the case for algebraic sum types, which require somewhat inconvenient wrapping or unwrapping.

Unlike algebraic sum types, set-theoretic union types are also not necessarily mutually exclusive. E.g. a number of type "Integer or Float" could be both. Moreover, set-theoretic types can be generalized to arbitrary Boolean combinations of types, e.g. "A or (B and not C)", possibly involving disjunctions, conjunctions, and negations (since {and, or, not} are functionally complete). That's not possible for algebraic sum/product data types. Though there is no mainstream language yet with a set-theoretic type system that supports negation types.

reply
This choice has nice ergonomics but brutal practical cost so that's why languages close to the metal do not want to do this and particularly don't want to go around defaulting to it whereas a language like Typescript is fine with it.
reply
Maybe it has "brutal practical cost", I don't know, but even most high-level languages don't have set-theoretic type systems. Perhaps because the approach is relatively new and most popular languages are old.
reply
Sum types in the set theoretic approach can be made with `(A & ¬B) | (¬A & B)`, or equivalently `(A | B) & (¬A | ¬B)`.

There's no mainstream language with negation types, but a few good research languages with them, eg MLstruct.

[MLstruct]:https://github.com/hkust-taco/mlstruct

reply
> MLstruct supports union, intersection, and complement (or negation) connectives. We make sure they form a Boolean algebra and add enough structure to derive a sound and complete type inference algorithm. For more details, please see the paper mentioned above.

This is cool. Unfortunately it's also too late now. The machines have taken over programming.

reply