Currently, inside of a generic type, when the name of that type is used without specifying the generic parameters, it is inferred to have the same generic parameters as the type it is inside. For example, inside an instance method, the use of the bare type name is the same as the type of self
, and inside a type method it is the same as the type on which it is called.
Most of the time I appreciate this convenience. However, there are some situations where the type name would ordinarily have its generic parameters inferred from contextāsuch as arguments passed to an initializerābut when that exact code is placed within an extension of the same type, suddenly the generic parameters are not inferred from context but are instead rigidly fixed to be those of the type itself.
As a toy example to demonstrate the issue, consider this function which wraps an Int
in an Optional
, then prints its debugDescription
:
func foo() {
let x = 1
let y = Optional(x)
print(y.debugDescription)
}
Pretty simple and straightforward. However, suppose this were instead a method on Optional
. If we write identical code:
extension Optional {
func foo() {
let x = 1
let y = Optional(x) // error: 'Int' is not convertible to 'Wrapped'
print(y.debugDescription)
}
}
ā¦we get a compiler error!
Everywhere else in the language we can write Optional(x)
and it works as expected, with the generic parameter being inferred from the value passed into the initializer, except here. Here itās a compiler error, because Optional
is treated as meaning the same type as self
.
In order to make the code compile, we have to spell out the generic parameter:
let y = Optional<Int>(x)
Now, weāre looking at just about the simplest possible case. If instead we were dealing with the result of chained Sequence
operations, we might have to write out some whole mess as the generic parameter:
Slice<ReversedCollection<LazyFilterSequence<LazyMapSequence<Range<Int>, Int>>>>
This is, frankly, absurd.
I consider it actively harmful that the syntax of āType name followed by initializer arguments from which the generic parameters are inferredā breaks when being used within that type itself. Calling an initializer on a type should work the same no matter where the code is located.