Hacker News new | past | comments | ask | show | jobs | submit login

If you have sum types, multiple return is less useful.

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.




My point is that you don't really know if the stdlib types actually have one method that returns 1 or 2 value or two methods one returning 1 value and the other returning 2 where the compiler chooses based on the call site which one to call. Just because [] looks like one method to the user doesn't mean it has to be one method.

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.


Tuples wouldn't be more expensive if implemented the way most languages do, where a tuple type has a fixed length and series of element types, e.g. '(Foo, error)'. In other words, structs with some syntax sugar.

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.)


maybe<Foo> or it's C++ equivalent optional<Foo> is more expensive. You now either have a bool + a value, or a pointer that can be nullptr. So you're consuming more memory at the very least and if you want to panic on accessing a value that doesn't exist that means an extra comparison as well. Granted there are situations the compiler can optimize this away in C++. I also find that starting to use optional in C++ code leads to it being used everywhere and for everything which introduces new run-time failure modes that can't be detected on compile time and in general IMO messes up the code.

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 :)


I was envisioning that the maybe-returning version would be a separate method, equivalent to the current two-return-value variant, which of course already has that overhead.

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 value


A maybe<T> in a language with halfway decent support for sum types is most likely a tagged pointer to a T, if it is reified at all.

A 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" ?


How do sum types make multiple return less useful? Multiple return is more like returning a single value of a product type—like a tuple or, less sexily, struct—than like returning a single value of a sum type. What am I missing?


Multiple return is almost always used as an optional/either type construct.

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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: