Having two different functions is a way of dealing with this while minimizing change as it's just syntactic sugar and not a language change. Variable length tuple types are a much bigger change. They'd also presumably be a lot more expensive. Ideally a map type implemented by the user should be as performant as the built-in map type.
But they're the wrong abstraction for this anyway. A better choice would be a type like maybe<Foo> (as mentioned in the post), that only lets you get at the contained Foo if it exists, rather than the current practice of returning a fake default Foo value in the case where it doesn't.
Having two overloaded functions would work, but it would increase the complexity of the language compared to dropping the overloading feature. Yes, for consistency you'd want to make that change to the builtin types as well, and that would be churn. But only in code that's already being churned: if generic collections are added (and maybe some immutability stuff), I think you'd want to inspect just about any code that uses slices or maps to see if a new collection type might work better or better follow the new idioms. (Mind you, I don't suggest actually breaking backwards compatibility, just leaving some of the existing stuff in a permanent supported-but-deprecated state. So "change everything that uses slices or maps" isn't as bad as it sounds - it'd be a recommendation, not a requirement.)
An alternative is to split find and access into two separate operations like C++ or to provide "in" like Python. Is there any language where a map/set access returns an optional/maybe?
FWIW I agree the current solution is clunky. It's clunkiness was evident prior to the template/generics question :)
Having the default return a 'maybe' would be a possibility; I'm pretty sure the practical performance difference would be completely negligible, given all the other stuff a map lookup has to do, but it might have worse ergonomics. In that case you'd probably want a builtin optional-unwrapping operator like some languages have, so it's not too verbose if you expect the element to be there.
> Is there any language where a map/set access returns an optional/maybe?
Swift is one. It has ! as an unwrap operator, along with other syntax sugar for optionals, so it's not verbose:
5> let q = ["a": "b"]
[snip]
6> q["a"]
$R2: String? = "b"
7> q["x"]
$R3: String? = nil
8> q["a"]!
$R4: String = "b"
9> q["x"]!
fatal error: unexpectedly found nil while unwrapping an Optional valueA clever compiler is likely to do the same thing for a pair of (bool, T): represent the bool as a tag in the pointer, store the T. If the value is reified at all.
What new run-time failure modes do you get with optional? Is it just what happens when you blithely ignore the "nothing" possibility and attempt to extract the contained value "on faith" ?
If you have a type for Some | None, then the return type of maps/chans/etc of (T, bool) aren't needed.
Similarly, the (T, err) returns can be replaced by a T | error sum type.
Sometimes multiple returns are used in other cases, but returning a struct covers those, or proper tuples where the value you return is a tuple.
I'm just claiming that multiple return is 90% of the time being used to patch over the lack of proper optional or either types.
if you have proper tuples and destructuring assignment, you can entirely remove that abomination.
Right now, it's impossible for a user to define a method that can return 1 or 2 values depending on how it's called.
Only stdlib types, like map and channel, get to do that.
It's fuggin horrific, and spreading that further to user defined functions would be a mistake.