You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
There are a number of options discussed in this document. In open discussions, we have been converging towards these choices:
External impl
Task: The type Song implements the interface Comparable. The methods of Comparable may not be used as unqualified members of Song. The implementation is allowed to live either in the library with Song or Comparable. Another use case is the "blanket impl" case, where we provide an implementation of the Printable interface for any type implementing the ToString interface, or an implementation of Comparable for Vector(T) anytime T implements Comparable. There are two possibilities under consideration:
out-of-line impl...as choice:
impl Song as Comparable {
method (me: Self) Less(rhs: Self) -> Bool;
}
impl (T:$ ToString) as Printable { ... }
impl Vector(C:$ Comparable) as Comparable { ... }
Advantages:
More concise, so less to read and write.
out-of-line external impl...as choice:
external impl Song as Comparable {
method (me: Self) Less(rhs: Self) -> Bool;
}
external impl (T:$ ToString) as Printable { ... }
external impl Vector(C:$ Comparable) as Comparable { ... }
Advantages:
More explicit to clarify that the the methods of this impl definition are not contributing to unqualified API of the type.
Matches how you would describe this kind of impl, especially when contrasting with "inline impl" (see below).
Note that this is a divergence from the Rust approach which uses for instead of as and puts the interface before the type (impl Comparable for Song). In discussions we have been favoring this as approach for two main reasons:
Song as Comparable is the actual name of the facet type we are defining the implementation of.
It seems more natural to express the parameters to the interface in terms of the parameters and associated items of the type than the other way around.
Conditional inline impl
Task: A Vector(T) doesn't implement the Printable interface or have a method Print unless T implements Printable:
struct Vector(T:$ Type) {
// data and methods ...
impl Vector(P:$ Printable) as Printable {
method (me: Self) Print() { ... }
}
}
Inline impl
Task: The type Song implements the interface Printable, and the methods of Printable may be used as unqualified members of Song. To preserve the "unqualified members all come (transitively) from declarations inside the type's definition" property, this means an impl block inside the definition of Song, but there are still two possibilities under consideration.
inline impl choice:
struct Song {
// data and methods ...
impl Printable {
method (me: Self) Print() { ... }
}
}
Advantages:
More concise, so less to read and write.
inline impl as choice:
struct Song {
// data and methods ...
impl as Printable {
method (me: Self) Print() { ... }
}
}
Advantages:
Consistency with the other uses of impl, which always put the interface name after as.
Matches the conditional impl syntax if you make the type between impl and as optional and default to Self.
Questions
Is this syntax good, or are there changes you would like to see? Do you prefer external for the out-of-line cases? Do you prefer to always use as for the inline cases?
The text was updated successfully, but these errors were encountered:
@zygoloid and I are both happy keeping as in the inline impl. It seems a small cost for consistency.
Both of us felt like the question around external was a bit of a bikeshed. My painter opinion is to keep it for now. If the annoyance ends up growing to the point that it outweighs the utility, we can remove it. But at least some folks have found the explicit marking to be useful.
I think we have consensus among the leads on this so closing as well. Happy to re-open if needed though!
There are a number of options discussed in this document. In open discussions, we have been converging towards these choices:
External impl
Task: The type
Song
implements the interfaceComparable
. The methods ofComparable
may not be used as unqualified members ofSong
. The implementation is allowed to live either in the library withSong
orComparable
. Another use case is the "blanketimpl
" case, where we provide an implementation of thePrintable
interface for any type implementing theToString
interface, or an implementation ofComparable
forVector(T)
anytimeT
implementsComparable
. There are two possibilities under consideration:out-of-line
impl
...as
choice:Advantages:
out-of-line
external impl
...as
choice:Advantages:
impl
definition are not contributing to unqualified API of the type.impl
, especially when contrasting with "inlineimpl
" (see below).Note that this is a divergence from the Rust approach which uses
for
instead ofas
and puts the interface before the type (impl Comparable for Song
). In discussions we have been favoring thisas
approach for two main reasons:Song as Comparable
is the actual name of the facet type we are defining the implementation of.Conditional inline impl
Task: A
Vector(T)
doesn't implement thePrintable
interface or have a methodPrint
unlessT
implementsPrintable
:Inline impl
Task: The type
Song
implements the interfacePrintable
, and the methods ofPrintable
may be used as unqualified members ofSong
. To preserve the "unqualified members all come (transitively) from declarations inside the type's definition" property, this means animpl
block inside the definition ofSong
, but there are still two possibilities under consideration.inline
impl
choice:Advantages:
inline
impl as
choice:Advantages:
impl
, which always put the interface name afteras
.impl
andas
optional and default toSelf
.Questions
Is this syntax good, or are there changes you would like to see? Do you prefer
external
for the out-of-line cases? Do you prefer to always useas
for the inline cases?The text was updated successfully, but these errors were encountered: