[swift-evolution] [Draft] Introducing StaticSelf, an Invariant Self
Patrick Smith
pgwsmith at gmail.com
Wed May 18 22:14:14 CDT 2016
Oh yep, I accidentally read that section as ‘Alternatives Considered’.
I still find this a little bit of a hack, so I hope this approach doesn’t get used too much, certainly not in the standard library. Is this only useful for factory or other static methods? It would just be a shame to see protocols become defensive for use cases like this.
I believe the protocol should remain ‘pure’, and the conforming types then decide on which way they want to go. If they use a static type (or label the method final), then its particular protocol requirements change.
Anyway, nice resolution!
> On 19 May 2016, at 1:06 PM, Matthew Johnson <matthew at anandabits.com> wrote:
>
>
>
> Sent from my iPad
>
> On May 18, 2016, at 9:57 PM, Patrick Smith <pgwsmith at gmail.com <mailto:pgwsmith at gmail.com>> wrote:
>
>> I think the distinction between StaticSelf and Self will be very confusing to newcomers.
>>
>> So the only reason why we must use StaticSelf instead of Self here is because we want NSURL to conform, and it isn’t final?
>>
>> protocol StringCreatable {
>> static func createWithString(s: String) -> StaticSelf
>> }
>>
>> I find it a code smell that this would affect the protocol and not the class.
>>
>> Why couldn’t you have this?
>>
>> protocol StringCreatable {
>> static func createWithString(s: String) -> Self
>> }
>>
>> extension NSURL: StringCreatable {
>> // can now conform conform because NSURL is fixed and matches the static
>> // type of the conforming construct. Subclasses need not re-implement
>> // NOTE: the return type can be declared as StaticSelf *or* as NSURL
>> // they are interchangeable
>> static func createWithString(s: String) -> StaticSelf {
>> // ...
>> }
>> }
>>
>
> You can't do this because the Self return type in the protocol requirement specifically *requires* all subclasses to override the method and return an instance of the subclass type.
>
> Nevertheless, we have identified a workaround that is similar enough to StaticSelf that we have abandoned the proposal. Please see The last couple posts in this thread if you're interested in the details.
>
>>
>>
>>> On 19 May 2016, at 3:37 AM, Erica Sadun via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>
>>> As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.
>>>
>>> https://gist.github.com/erica/995af96a0de2f2f3dc419935e8140927 <https://gist.github.com/erica/995af96a0de2f2f3dc419935e8140927>
>>>
>>> -- E
>>>
>>>
>>>> On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>
>>>>>
>>>>> On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>
>>>>> Matthew Johnson via swift-evolution <swift-evolution at ...> writes:
>>>>>
>>>>>> 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.
>>>>>
>>>>> You can do that today:
>>>>>
>>>>> protocol Makable {
>>>>> associatedtype MadeType
>>>>> static func make(value: Int) -> MadeType
>>>>> }
>>>>>
>>>>> func makeWithZero<T: Makable where T == T.MadeType>(x: Int) -> T {
>>>>> return T.make(value: 0)
>>>>> }
>>>>>
>>>>> You can't currently constrain MadeType to be the same as the conforming
>>>>> type, but, does it matter? What kind of extra guarantees would that give,
>>>>> since you need to add the extra constraint anyway in generic code?
>>>>
>>>> Wow, this is pretty cool. Thank you very much for pointing this out Nicola!
>>>>
>>>> I haven’t seen this approach to solving the problem. Given the amount of discussion this problem has received I am surprised nobody has shared this solution yet. I just checked in Xcode 7.3 and it works there. It isn’t dependent on any pre-release features.
>>>>
>>>> Instead of using StaticSelf under the current proposal:
>>>>
>>>> protocol StringInitializable {
>>>> static func initializeWith(string: String) -> StaticSelf
>>>> }
>>>>
>>>> We just add an associatedtype defaulted to Self:
>>>>
>>>> protocol StringInitializable {
>>>> associatedtype Initialized = Self // where Self: Initialized
>>>> static func initializeWith(string: String) -> Initialized
>>>> }
>>>>
>>>> extension NSURL: StringInitializable {
>>>> static func initializeWith(string: String) -> NSURL {
>>>> return NSURL()
>>>> }
>>>> }
>>>>
>>>> func makeWith<T: StringInitializable where T == T.Initialized>(string: String) -> T {
>>>> return T.initializeWith(string: string)
>>>> }
>>>>
>>>> There are two minor downsides to this approach:
>>>>
>>>> 1. You can’t copy and paste the method signature.
>>>> 2. You can theoretically conform a type completely unrelated to `Initialized` to the protocol, thus violating the semantics.
>>>>
>>>> I think we can live with these downsides. Maybe the `Self: Initialized` will be possible someday. That would be pretty close to StaticSelf. The only difference would be that subclasses still have flexibility to override with their own type.
>>>>
>>>> Now that a reasonable way to do this with existing language features has been identified I will withdraw this proposal. If this approach doesn’t address use cases others have in mind for StaticSelf please speak up!
>>>>
>>>> Doug, if you’re reading this, does the `where Self: Initialized` (i.e. arbitrary subclass constraints) fall into the scope of your “completing generics” manifesto? This is a concrete use case that would utilize subclass constraints.
>>>>
>>>> -Matthew
>>>>
>>>>
>>>>>
>>>>> Nicola
>>>>> _______________________________________________
>>>>> swift-evolution mailing list
>>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>>>
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org <mailto:swift-evolution at swift.org>
>>> https://lists.swift.org/mailman/listinfo/swift-evolution <https://lists.swift.org/mailman/listinfo/swift-evolution>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160519/6d43382e/attachment.html>
More information about the swift-evolution
mailing list