> On May 13, 2016, at 3:12 PM, Joe Groff <[email protected]> wrote:
>
>>
>> On May 13, 2016, at 9:06 AM, Matthew Johnson <[email protected]> wrote:
>>
>>
>>> On May 13, 2016, at 10:55 AM, Joe Groff <[email protected]> wrote:
>>>
>>>
>>>> On May 13, 2016, at 8:18 AM, Matthew Johnson <[email protected]>
>>>> wrote:
>>>>
>>>> When I write a class Base with non-final methods that return instances of
>>>> Base I can choose whether to state the return type as Self (covariant) or
>>>> Base (invariant, under this proposal StaticSelf would also be an
>>>> alternative way to state this). If I choose to specify Base as the return
>>>> type derived classes *may* override the method but are not required to.
>>>> Further, if they *do* override the method they are allowed to choose
>>>> whether their implementation returns Base or Derived.
>>>
>>> `StaticSelf` requirements by themselves don't even save you from
>>> covariance. If Base conforms to a protocol (with Self == Base), Derived
>>> inherits that conformance and also conforms (with Self == Derived). If
>>> `StaticSelf` always refers to the conforming type, then it must also be
>>> bindable to Base and Derived, so a base class must still use a
>>> covariant-returning method to satisfy the `StaticSelf` requirement.
>>
>> We are specifying that `StaticSelf` refers to the type that explicitly
>> declares conformance. If a class inherits conformance it refers to the base
>> class which explicitly declared the conformance it is inheriting.
>
> That makes `StaticSelf` tricky to use in generic code. This would be invalid:
>
> protocol Makable {
> static func make(value: Int) -> StaticSelf
> }
>
> func makeWithZero<T: Fooable>(x: Int) -> T {
> return T.make(value: 0) // ERROR: T.StaticSelf may be a supertype of T
> so isn't convertible to T
> }
I agree it’s a bit tricky. But that’s better than not possible at all. You
just need a typealias and a same type constraint to make this work as expected
/ desired:
protocol Makable {
typealias RootMakable = StaticSelf
static func make(value: Int) -> StaticSelf
}
func makeWithZero<T: Makable where T == T.RootMakable>(x: Int) -> T {
return T.make(value: 0) // works now
}
Now that we have a typealias we can refer to the binding of StaticSelf and
constrain it as necessary for whatever purpose we have in mind. In some cases
that will be a same type constraint so that our code works properly with class
clusters. I don’t have concrete examples of other use cases but can imagine
use cases constraining the typealias to a protocol, for example.
If we had control over inheritance of conformance at the point of conformance
we probably wouldn’t be talking about StaticSelf. But we don’t and this is a
problem that has caused enough people trouble that it is worth solving.
StaticSelf does that in a general way that is also as a shorthand in types
themselves and has consistent semantics in both use cases.
IIRC the design of point-of-conformance control over inheritance of conformance
is pretty thorny. I wouldn’t mind seeing that feature eventually but don’t
have any confidence that it will come soon.
>
> `StaticSelf` in this model is effectively an associated type of the protocol,
> with a `Self: StaticSelf` constraint (if that were supported).
If you add that the associated type is automatically bound with the initial
conformance (and cannot be modified by subclass conformances) then yes, you can
look at it this way.
>
> -Joe
_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution