i had always assumed we cannot overload a property because this is a compiler error:
public
struct S
{
public
var readConcern:Int { 1 }
public
var readConcern:Int? { 1 }
}
// error: invalid redeclaration of 'readConcern'
and yet i am able to provide overloads in a generic context, and access those overloads in a concretely-typed context:
public
protocol SessionCommand
{
var readConcern:Int? { get }
}
public
protocol ReadCommand:SessionCommand
{
var readConcern:Int { get }
}
extension ReadCommand
{
public
var readConcern:Int?
{
.some(self.readConcern)
}
}
public
struct S:ReadCommand
{
public
var readConcern:Int { 1 }
}
func test(s:S) -> Int?
{
if let r:Int = s.readConcern
{
return r
}
else
{
return s.readConcern
}
}
Assuming overload by return value is a good thing (which is questionable) this behaviour is already strange: I can do it with a function and with a subscript, but not with a variable?
it makes it easier to add and remove the access modifiers (public, private, etc.) using editor line operations, which i do quite often. also i personally find it easier to scan for the property names if they all begin at n + 4 columns from the base indent of the type.
If while evaluating a conformance we find multiple candidates with the same name as a protocol requirement, we pick the one with the matching type, if possible. So in this case it’s going to be the default implementation in the protocol extension.
If Swift didn’t allow this, how would it handle the case where either 1) Pear.name exists from the start, but Fruit.name is added in a future release, or 2) Pear gains a retroactive conformance to Fruit?
The redeclaration checking rules are meant to reject situations such as duplicate overloads that cannot be distinguished by name or type, but they don’t cover all potentially confusing situations.
It’s not even about stored properties per se, just that we allow overloading of property declarations when extensions are involved. Stored properties cannot appear in extensions so you cannot have two stored properties with the same name as a consequence of those two behaviors.
Certainly not every behavior of the implementation has a logical explanation :) But in this case I think it’s pretty reasonable.
You can also just have two constrained extensions that declare computed properties with the same name and type. If the generic signatures are distinct it’s not considered to be a redeclaration, even though you might still end up in a situation where some concrete generic arguments satisfy both signatures, at which point it would be diagnosed as an ambiguity at the call site.
The second statement is guiding Swift to pick the declaration from the protocol by providing additional contextual type information.
This feature proves quite useful, for example, when you want to specify the type of a generic function's return value.
func peek <T> (_ u: Any) -> T? {
u as? T
}
Someone with C++-hat firmly on would automatically write this:
let u = peek <Int> (bar ()) // Cannot explicitly specialize a generic function
And they are relieved when they discover that the following actually works.
for _ in 0..<3 {
if let u: Int = peek (bar ()) {
print (u)
}
if let u: String = peek (bar ()) {
print (u)
}
if let u: [Int] = peek (bar ()) {
print (u)
}
}
func bar () -> Any {
let u: [Any] = [3, "three", [1, 1, 1] ]
return u [Int.random(in: 0..<u.count)]
}