On 12-08-17 10:25 AM, Nathan wrote:

Yes, this is true, but it doesn't address the general problem:

match myfoo {
   [bar, 42] => /* ... */
}

I may have the rust syntax wrong, but the point is anywhere in a
structured/compound pattern either a variable binding or an enum
discriminator may appear, right?

Yes. Or a constant. I.e. 'some(red)' doesn't match 'some(blue)' if red and blue are named constant integers (or enum tags).

I think it's going to be a bit of a losing battle to differentiate these in all cases; you wind up having to make one or more "very frequently written thing" either very ugly or likely to be misused.

Now, curiously, this is not as frequently _ambiguous_ as it seems. Most cases are automatically unambiguous:

  - foo::bar => ...  is unambiguous due to '::'
  - foo(_) => ... is unambiguous due to '(_)'
  - ref foo => ... is unambiguous due to 'ref'
  - copy foo => ... is unambiguous due to 'copy'

The only case we're actually looking at is nullary-constructor or equality-with-a-constant. That is, someone writing:

  match ... {
    nonr => ...
  }

and matching 'some(x)' against it because they fat-fingered 'none' and wound up binding an identifier (or alternatively, they mentioned an ident they thought was a constructor-in-scope but it was not, so they introduced a binding).

Fixing this is Hard though. We struggled a lot. You have this sequence of painful logic:

  - Hard requirement: 'let x = 10' declares a variable.
  - Hard requirement: 'const x : int = 10' declares a constant.
  - Hard requirement: 'x' in expression position refers to a variable
    and/or a constant, at least.

These are very basic "if we change them, the language passes the threshold of too-ugly-to-write". So they're not on the table. Now consider:

  - Misuse avoidance #1: users tend to forget that patterns and exprs
    are different, since they "look similar", so anything they write as
    an expr they're likely to write as a pattern. In particular, 'x' as
    a constant (and 'x' as a nullary enum ctor, if those are unadorned).

  - Misuse avoidance #2: users tend to write all enum ctors the same
    way, nullary and non, so if 'none' has a sigil, 'some' has to have
    one too, otherwise they'll wind up writing 'none'-without-the-sigil
    just out of symmetry with 'some'.

  - Ergonomics #1: ideally the distinction between a constant integer
    like "const red : int = 0xff0000;" and an enum ctor can be forgotten
    by users. People change between magic constants and enums with
    some frequency when writing code.

  - Ergonomics #2: ideally enum ctors don't all have sigils in expr
    position, since a great many are unambiguously ctor-calls anyway
    (eg. 'some(10)' is much nicer than '`some(10)' everywhere).

Misuse avoidance #1 makes it pretty much impossible to dodge the constant-vs-binding ambiguity, and the combination of #1 and #2 second means that the only dodge likely to work on the enum-vs-binding ambiguity is one where _all_ occurrences of _all_ ctors require sigils (eg. the Ocaml `Variant thing, for nullary and N>1-ary, in pattern and expr forms alike). This is possible. But it's about the only solution I can see that doesn't bring more problems than it solves. And even if we did that, it wouldn't solve #1, and would lose ergonomic arguments #1 and #2.

So, with all this in mind, we went with the SML rule: having the compiler restrict names introduced-by-a-pattern to not-collide with any in-scope nullary ctors and constants. It _seems_ to be working pretty well in practice.

-Graydon
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to