This seems like a no brainer to me, +1.
- Does this example not missing the
where T: Hashable
constraint? - And it shouldn't be
<T...>
as we have no such generic syntax yet.
extension <T: Hashable> (T...): Hashable { ... }
Overall a very welcoming addition to the language. +1 for me as well.
Indeed, if you only want tuples to conform to Hashable
when each element is Hashable
, it's missing a T: Hashable
There is also no such extension <T>
syntax yet. That example in the Future Directions section is meant to show how parameterized extensions could potentially compose with variadic generics.
I'm very much in favor of this pitch, it's a natural extension to the language (no pun intended lol) and it removes a weird sharp-corner I've encountered a number of times.
I was wondering about this example in the future directions section:
Wouldn't this actually be equivalent to the following? Or am I misunderstanding?
extension Array where Element: Equatable { ... }
It seems like this would/should be allowed already because of SE-0328. But, I'm having trouble seeing how all the various proposals actually interact with each other.
The main proposal seems very straight forward, makes you wonder why extension [String]
wasn't always possible. But could you elaborate on what parametrized extensions would do?
This for example seems to be the same as just omitting the <Wrapped>
? I mean if you extend Array
, you get access to Element
, so presumably if you extend Array<Optional>
you would get access to Element.Wrapped
? OR perhaps you wouldn't be able to write extension Array<Optional>
at all without this addition?
You literally can't express this in the language otherwise. You might be able to spell it as Array<Optional<some Any>>
, but that would already be sugar for the parameterized extension, so you might as well support the long form.
Let's just see what it would look like without parameterized extensions or the proposed sugar:
extension Array where Element == Optional<???> {
What would you even put there?
Why not simply extension Array<Optional>
? We already allow extending generics without specifying what the generic parameter should be, so I would expect this to work in nested positions too.
If we want to constrain the nested type, then I would instinctively assume that this would work:
extension Array<Optional> where Element.Wrapped: P
I mean I think it's strange that Element
just pops up from nowhere when you extend an Array, it would have made more sense to me with a syntax like
extension Array<T> where T: Codable
but given that it's currently written
extension Array where Element: Codable
we might as well support nesting.
Overall +1
I do foresee some confusion around this for newcomers in the future:
extension Array<Element> {} // error: Cannot find type 'Element' in scope
It would be a very natural thing to try, but I acknowledge the ambiguity if it was allowed.
Maybe we should just... allow this?
In the case where there's no externally-defined Element
type, (I think) it would be unambiguous. In the case where there is an externally-defined Element
type, we could force users to disambiguate by either dropping the <Element>
(if they meant to extend the unbound Array
type) or specifying Array<MyModule.Element>
(if they really did mean to extend the bound type).
Not sure what the source compatibility impact of this would look likeâit wouldn't be great to start warning on a bunch of code that used to be fine. But if constructions like this end up rare, maybe it would be worth it for the sake of consistency.
OK so maybe allowing until disambiguation is required would work;
struct Element { }
extension Array<Element> {} // error: Cannot disambiguate Element from type parameter Element and struct Element.
And then fix with either;
extension Array { } // extend with type parameter
extension Array where Element == Element { } // extend with bound type struct Element
I'm still unsure if its clearer or more complicated.
I think the second fix would have to be
extension Array<MyModule.Element> {
since it wouldnât make much sense for the Element
on each side of the ==
to mean something different, IMO.
I think that in our attempt to avoid confusion we would cause more. By allowing Array<Element> we would have to introduce complicated rules with regard to disambiguation. This could also make it harder for the compiler to resolve Element if there are already errors in the program, making the development experience worse. Further, as you mention:
So if this werenât allowed, the rule that Array<T> == Array where Element == T would essentially be broken (or at least obscured).
But this is certainly a good opportunity to make sure diagnostics guide users in the right direction.
I think the proposal as-written already breaks the simplest version of this model, because the following compiles just fine today (albeit with a 'redundant constraint' warning):
struct Element {}
extension Array where Element == Element { // extension Array
var y: Int { 1 }
}
Array<Int>().y // 1
Further, regardless of whether extension Array<Element>
is allowed in the unambiguous case (i.e., when there's not an actual Element
type in scope), I think we should offer a warning under this proposal when the user attempts to write it:
struct Element {}
extension Array<Element> {} // warning
something like:
warning: extending the bound generic type 'Array<MyModule.Element>'
fix-it: if this is intended, fully qualify the type to silence this warning (add 'MyModule.')
fix-it: if you meant to extend the unbound 'Array', remove '<Element>'
That is to say, I don't think that disallowing extension Array<Element>
rescues us from the "complicated rules" required for disambiguation (which I don't actually think are that complicated ).
Given that a large part of the motivation of this proposal is parity with new generics features, and we don't allow extension Collection<Element>
, we probably don't need to resolve the question of extension Array<Element>
as part of this proposalâwe'd probably be better off addressing for both protocols and generic types down the line.
But what would be the point of writing extension Array<Element>
if Element
is the generic type from Array
. Isn't it exactly the same as writing extension Array
?
I donât think itâs clearly the right choice, but there are some things I like about it:
- mirrors the declaration syntax
struct Array<Element>
- perhaps makes clearer that uses of
Element
inside the extension are referring to the generic parameter and not some externalElement
type - people may be drawn to it more since this proposal allows the
extension Array<GenericArg>
syntax
That said, there are definitely drawbacks to allowing it (it would plausibly be more confusing if bound and unbound extensions use the same syntax differentiated based only on name), so itâs not an obvious home run.
Anyway, Iâve convinced myself itâs orthogonal enough to the pitch at hand that I probably wonât discuss it more in this thread, in the interest of staying on-topic.
Good point. Perhaps Iâd be more appropriate to say that the rule is that
extension Array<T> {}
// Is equivalent to:
extension Array where Self.Element == T {}
Agreed.
Heh, only in the specific case of Array
since Array
defines a typealias Element = Element
. But in the fully general case, generic parameters aren't members, so this, for instance, fails:
extension Array where Self.Element == Int {}
struct S<T> {}
extension S where Self.T == Int {} // error: 'T' is not a member type of generic struct 'S<T>'
Big +1.
A syntax like extension Array<String>
feels like it always has been there.
Good for beginners. Also in general. It brings consistency and brevity.
But not sure about extension for sugars: [String]
. Feels doing something clearly wrong.
However at the same time I'm not particularly against, since it might be just I'm not used to it.
Also feeling a connection with this pitch: [Pitch] Use âsomeâ to express âsome specialization of a generic typeâ - #10 by GreatApe
I remember earlier days: I was a beginner but began to understand some part of it and tried to create extensions for Array
.
extension Array<String> { ... // error
Then I was like
This is now under review! Please take any further discussion over there.