[swift-evolution] [Draft] Introducing StaticSelf, an Invariant Self

Matthew Johnson matthew at anandabits.com
Sat May 14 09:35:12 CDT 2016


> On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <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
> https://lists.swift.org/mailman/listinfo/swift-evolution



More information about the swift-evolution mailing list