Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keyword to use in place of is in where...is constraints #2495

Closed
josh11b opened this issue Dec 22, 2022 · 9 comments
Closed

Keyword to use in place of is in where...is constraints #2495

josh11b opened this issue Dec 22, 2022 · 9 comments
Labels
leads question A question for the leads team

Comments

@josh11b
Copy link
Contributor

josh11b commented Dec 22, 2022

Summary of issue:

A question arose in proposal #2483 about what keyword should be used to replace is. The particular syntax in question is when expressing a constraint that a type variable implements an interface or satisfies a named constraint, for example where .ElementType is Ordered. (It should be noted that this case is not expected to be particularly frequent.)

Proposal #2483 suggests replacing is with impls, but @jonmeow raised concerns that would be ambiguous with the plural of impl. My take is that there is a relationship between these two words that I want to preserve, that an impls constraint can be satisfied by writing an impl declaration, and in the examples I've looked at I had no problem disambiguating. I expect this is because this ambiguity commonly arises in English for words (like "pile") that can be used as both nouns and verbs, and so I have a lot of practice disambiguating.

I've also raised this question in the #naming channel on Discord.

@josh11b josh11b added the leads question A question for the leads team label Dec 22, 2022
@zygoloid
Copy link
Contributor

zygoloid commented Jan 4, 2023

Another possibility would be to use the same syntax that we use in an interface or named constraint:

  • where T is C becomes where impl T as C
  • where .Self is C becomes where impl as C

This approach would also give syntax for something that's hard to write at the moment:

  • AddWith(i32) where extends ImplicitAs(.Self.(AddWith(i32).Result)), meaning the same thing as AddWith(i32) & ImplicitAs(.Self.(AddWith(i32).Result)), except the latter doesn't type-check because we don't know that .Self implements AddWith(i32) early enough.

However, where T impls C seems more readable to me, and better parallels the syntax of other where constraints.

@jonmeow
Copy link
Contributor

jonmeow commented Jan 4, 2023

I expect this is because this ambiguity commonly arises in English for words (like "pile") that can be used as both nouns and verbs, and so I have a lot of practice disambiguating.

I want to directly address this, because I think this is in conflict with Carbon's "Code that is easy to read, understand, and write" goal. We're going to have a lot of non-native, even non-fluent English speakers using Carbon. I find myself having trouble, on a re-reading, understanding whether "Parameterized impls" means "Parameterized impls" or "Parameterized impls" (for example, what is the context in the table of contents that lets you disambiguate? Should I just know which it means based on experience?). I worry people who are both less familiar with English and less familiar with Carbon will find this to be a pretty steep learning curve, and an ongoing cost to require more context.

Rewriting the documentation to be more specific, replacing all uses of "impls" with either "impls" or "impls", partly addresses this. It'll still require closer reading and writing, but can't address spoken context.

I want to be clear here, my concern is very much about non-code contexts. In source code, impl versus impls is enforced by parsing. However, source code has context that other situations don't.

(edit: maybe more the ecosystem goal, given that's the only mention of documentation?)

@josh11b
Copy link
Contributor Author

josh11b commented Jan 26, 2023

Whatever we decide here, I think we should be consistent with extend/extends. It would be weird to have impl and extends be peers in a class definition.

@chandlerc
Copy link
Contributor

This came up while discussing #995 and @mconst and I brainstormed some ideas.

First, @mconst also found impls a bit confusingly similar to impls, so growing data points around the realistic chance of confusion here.

Things we considered:

  • T is C
  • T isa C
  • T impls C
  • T implements C
  • T ~ C
  • T: C
  • T as C
  • T impl C
  • T impl as C
  • impl T as C

Lots of these were "fine" but I don't think any really sparked much joy.

Some specific concerns I remember coming up...

  • isa seems (much) too rooted in inheritance.
  • implements is long but otherwise fine.
  • ~ is already being considered for something a bit more fitting -- bidirectional convertibility of our type "equality" constraints.
  • T: C seems to not align with the rest of Carbon's grammar and use of :.
  • impl seems a bit clunky, and surprising to see in this position when it usually isn't.
  • impl as seems even more clunky
  • impl T as C also seems even more clunky, and would be confusing with the rules around rewrite constraints (different here from the use of impl T as C in a type).

The form that I find myself coming back to and liking more is T as C.

Advantages of T as C:

  • Already a thing, and names the facet that is required to exist.
  • Already used in a facet-like-but-not-facet context for impl T as C { ... }.
  • Short, easy to read, etc.

Disadvantages:

  • Doesn't read as nicely in context: T:! C where C.ElementType as AddWith(i32)
  • Underlying the above disadvantage, it doesn't fit into the model of a boolean expression that should be true. Instead, it is a cast that should be possible or meaningful, which is somewhat different from the rest of the things in a where clause.
    • However, "rewrite" constraints don't quite fit this model either.
    • When using == constraints, they don't actually imply any boolean expression that would return true. In fact, at least my understanding is that the T == U constraint could be written as a boolean expression but it would return false even when the constraint holds.
      • A consequence of this is that we've been also been considering moving away from == for type equality constraints as they aren't transitive or truly "equal".

Overall, I'm not sure the disadvantages outweigh the advantages, and I'm somewhat leaning towards as. Curious to hear other thoughts here though as I don't think we had enough folks at the point we got to this in open discussion to really get a good feeling for what direction would broadly work best here.

@mconst
Copy link
Contributor

mconst commented Feb 6, 2023

@chandlerc and I chatted about this a bit more on Discord. Here's a quick summary -- hopefully this accurately represents your opinion!

Neither of us are thrilled with how where T as I reads; it would be nice if we could find something better.

My preference is where T: I, which is easy to read and matches Swift and Rust. It also matches the way you'd write the same constraint in an ordinary declaration, T:! I. But @chandlerc doesn't like that syntactic similarity, because the where clause and the declaration have subtly different semantics: the where clause doesn't make the names from I available in T, and it doesn't propagate where .A = B rewrites from I to T. So @chandlerc would prefer a distinct syntax that doesn't look like a declaration, to reflect those semantics. (My feeling is that the subtle semantic differences between where clauses and declarations are surprising regardless of what syntax we use, so we should just pick whatever syntax is most intuitive and readable.) There's also some concern that the use of : would make parameter lists hard to read when they contain embedded where clauses, like T:! Container where .ElementType: Printable, U:! OtherConstraint.

As for other options, we both think where T implements I is pretty good aside from being long. implements could also work as a general-purpose boolean operator to query whether a type implements an interface.

@zygoloid
Copy link
Contributor

zygoloid commented Feb 8, 2023

@josh11b, @chandlerc, and I discussed this further today, considering a variety of options: T is C, T implements C, T as C, T: C, T impls C, T isa C, and T imps C.

After weighing up all of these options, we are leaning towards T impls C, despite its challenges, with the idea that we would take other action to avoid impls being confused with impls. Specifically, the idea is to use impl only as a verb to say that a type implements a constraint, or in a compound noun such as "an impl declaration" or "the impl keyword", and never as a noun by itself to refer to an impl declaration: instead of saying "the type MyType has two impls, for InterfaceX and InterfaceY", we would say things like "the type MyType has two impl declarations" or "the type MyType impls InterfaceX and InterfaceY".

@mconst
Copy link
Contributor

mconst commented Feb 8, 2023

Sounds good! For what it's worth, as one of the people who found impls a bit hard to read, I'm not sure it's necessary to avoid using impl as a noun. In my case, at least, I don't think it would have helped -- impls was unfamiliar when I first saw it, but I'm sure I'll get used to reading it, even if we also continue talking about impls like normal.

(Another data point: in Rust it's very common to use impl as a noun, both singular and plural, but someone also wrote a macro named impls! to test whether a type implements a trait.)

@chandlerc
Copy link
Contributor

Adding a bit more of the rationale from the discussion:

  • We're worried about the verbosity of implements. It's rare that we worry about this, but this specific case seems likely to have an unfortunate negative impact.
  • Most other spellings we looked at were confusing in some or many ways. We'd rather a syntax that doesn't risk appearing to mean something other than what it does.

FWIW, if we had another spelling that didn't have the above two problems, I think we'd be delighted to not bump into the confusion problems of having both impl and impls, but so far no one has found a spelling that we didn't ultimately end up disliking a bit more due to these two issues.

@chandlerc
Copy link
Contributor

Marking this as decided -- none of the leads are delighted here, but we're all ending up in the same point in the tradeoff space so let's ship it.

josh11b added a commit to josh11b/carbon-lang that referenced this issue Mar 16, 2023
josh11b added a commit to josh11b/carbon-lang that referenced this issue Sep 22, 2023
github-merge-queue bot pushed a commit that referenced this issue Sep 23, 2023
Includes proposals:
- #990
- #2188
- #2138
- #2200
- #2360
- #2760
- #2964
- #3162

Also tries to use more precise language when talking about:
- implementations, to avoid confusing `impl` declaration and definitions
with the `impls` operator used in `where` clauses, an issue brought up
in #2495 and #2483;
- "binding patterns", like `x: i32`, and "bindings" like `x`.

---------

Co-authored-by: Chandler Carruth <chandlerc@gmail.com>
github-merge-queue bot pushed a commit that referenced this issue Oct 3, 2023
Continued from part 1: #3231. Second step updating
`docs/design/generics/details.md`. There remains some work to
incorporate proposal #2200.

- The biggest changes are incorporating much of the text of proposals:
  - #2173
  - #2687
- It incorporates changes from proposals:
  - #989
  - #1178
  - #2138
  - #2200
  - #2360
  - #2964
  - #3162
- It also updates the text to reflect the latest thinking from leads
issues:
  - #996
  - #2153 -- most notably deleting the section on `TypeId`.
- Update to rule for prioritization blocks with mixed type structures
from [discussion on
2023-07-18](https://docs.google.com/document/d/1gnJBTfY81fZYvI_QXjwKk1uQHYBNHGqRLI2BS_cYYNQ/edit?resourcekey=0-ql1Q1WvTcDvhycf8LbA9DQ#heading=h.7jxges9ojgy3)
- Adds reference links to proposals, issues, and discussions relevant to
the text.
- Also tries to use more precise language when talking about
implementations, to avoid confusing `impl` declaration and definitions
with the `impls` operator used in `where` clauses, an issue brought up
in
  - #2495 
  - #2483

---------

Co-authored-by: Richard Smith <richard@metafoo.co.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
leads question A question for the leads team
Projects
None yet
Development

No branches or pull requests

5 participants