inko-lang / inko

A language for building concurrent software with confidence
http://inko-lang.org/
Mozilla Public License 2.0
855 stars 38 forks source link

Come up with something better than the "try" keyword #635

Open yorickpeterse opened 10 months ago

yorickpeterse commented 10 months ago

Description

Inko supports the syntax try expression, which is syntax sugar for a match on an Option or Result. The purpose is to reduce the boilerplate of "unwrap if OK, throw if not" that comes with explicit match expressions.

Unfortunately, the syntax doesn't compose well. First, the prefix nature of the syntax means that in calls chains you end up having to nest try expressions. Second, while we used to also support try { expr }, we currently don't. The result is that for the expression x.y.z, where x.y and (x.y).z may throw, you end up with this:

try (try x.y).z

The second problem is that try only supports Result and Option, and this is implemented in the compiler. Adding support for other types would require something like Rust's Try trait, which is something I want to avoid given the type soup/complexity this introduces.

Finally, try introduces a second way of doing pattern matching, but with a more specific purpose. While we might not be able to avoid this, it would be nice if we'd only have the match keyword for pattern matching, with the added benefit of it supporting all types.

Long story short: I'd like to explore alternatives that:

  1. Are usable in method call chains
  2. Are easy to spot (i.e. no foo.bar?.baz nonsense)
  3. Support any enum type, without special-casing such types in the compiler (as is done with Option and Result at the moment)
  4. Don't require you to implement a whole bunch of traits and what not

Related work

yorickpeterse commented 10 months ago

Idea 1:

When defining an enum, at most one variant can be marked with else, but only if there are exactly two variants, and if the OK/not-else variant wraps exactly 1 argument, like so:

class enum Result[T, E] {
  case Ok(T)
  case else Error(E)
}

The try expression then translates to a match where the OK case matches against the case without else, and the error case against the else case.

The syntax and rules are a bit wonky, but it would allow for custom types to be used with try, without needing to implement a complicated trait.

yorickpeterse commented 10 months ago

Idea 2:

Change the semantics of try expr such that we allow try { expr } again, and such that try a.b.c results in try being applied to each "step" that returns a Result or Option, and thus is equivalent to try (try (try a).b).c.

This makes chaining easier, in exchange for error handling being less localized, and potentially confusing diagnostics if different types are returned.