is with implsUse the keyword impls instead of is when writing a where constraint that a
type variable needs to implement an interface or named constraint.
What was previously (provisionally) written:
fn Sort[T:! Container where .ElementType is Ordered](x: T*);
will now be written:
fn Sort[T:! Container where .ElementType impls Ordered](x: T*);
The is keyword has been used as a placeholder in where constraints
expressing that a type variable is required to implement an interface or named
constraint. It has been an open question what word should be used in that
position since its original adoption in
#818. Since then,
reasons to use a different word in this position have been discovered:
is as a keyword for another purpose.is came from Swift where x is T means "x has the
type T". With the changes to Carbon generic semantics, particularly
#2360: Types are values of type type,
that is increasingly a poor fit for what we mean by this condition.The is keyword as a constraint operator was introduced in
#818: Constraints for generics (generics details 3),
along with the open question about how to spell it.
The choice of is in that proposal followed
is being Swift's type check operator,
where x is T is true if x has type T. Note that there are differences
between the is operator in Swift and what we have used it for in Carbon. In
Swift, it is used to test whether a value dynamically has a specific derived
type, when you have a value of a base class type and are using inheritance. In
Carbon:
Use the keyword impls instead of is when writing a where constraint that
at type variable needs to implement an interface or named constraint. The
specific changes are included in
the same PR as this proposal.
This proposal is working towards Carbon's code that is easy to read, understand, and write goal:
impls
keyword, commonly matching how a comment would describe the constraint.where T impls C constraint that is not satisfied for
some calling type T, the fix is for the caller to add an impl T as C
definition.One concern with using impls is the potential for confusion with the plural of
impl, meaning "implementations," rather than acting as the verb "implements."
We hope to mitigate that concern by avoiding use of "impls" to mean anything
other than impls in our documentation. For example, we would say "impl
declarations" instead of "impls".
Alternatives were considered in
#2495: Keyword to use in place of is in where...is constraints.
A number of alternatives were considered:
T is CT isa CT impls CT implements CT ~ CT: CT as CT impl CT impl as Cimpl T as CThe reasons against is were outlined in the problem and
rationale sections. Reasons against other alternatives:
isa seems (much) too rooted in inheritance.implements is long but otherwise fine. This is a specific place where
being verbose has worrisome negative impact.~ is already being considered for something a bit more fitting --
bidirectional convertibility of our type "equality" constraints.impl seems a bit clunky, and surprising to see in this position when it
usually isn't.impl as seems even more clunkyimpl 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).In general there were concerns that the alternatives were confusing and risk appearing to mean something other than what it does.
T as CThe keyword as received more consideration:
impl T as C { ... }.It had some disadvantages, though:
impl to satisfy the constraint.T:! C where C.ElementType as AddWith(i32). This was a big consideration in
deciding against using as.where clause.
== 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.T: CT: C matches how Swift and Rust write this, and is similar to the way you'd
write the same constraint in an ordinary declaration, T:! I. This syntactic
similarity is also a liability due to the differences in semantics when used in
a where clause: 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.
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.