[Draft] Introducing StaticSelf, an Invariant Self

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

-- E

···

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Geeat solution! Thanks to Nicola for posting it!

Matthew Johnson via swift-evolution <swift-evolution@...> 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.

+1 for being able to constrain the generic type. I've been missing that in my little graph library.

-Thorsten

···

Am 14.05.2016 um 16:35 schrieb Matthew Johnson via swift-evolution <swift-evolution@swift.org>:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

-Matthew

Nicola
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

It strikes me that in a sense, the proposed StaticSelf is basically what '_' does

By specifying StaticSelf we are trying to tell the compiler to ignore what is going on that that precise location to revert to something expressed 'earlier'. This is somehwat anologous to what we do when we use '_' in signatures: we escape the specifics of the current location, ony in that case there is no prior context to refer to, so the parameter winds up having no particular name.

If you try to read the examples with this interpretation: "nothing specific from this location" then you can see how you wind up 'inheriting' the previously expressed static type of self. So I would seriously consider using _ instead of adding StaticSelf.

Regards
(From mobile)

···

On May 18, 2016, at 7:37 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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 {
     // ...
}
}

···

On 19 May 2016, at 3:37 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

As I understand, the initial main idea of StaticSelf(actually it was self at the beginning) was just static replacement for the name of currently defined type(i.e. as placeholder for the name of type):

>----------------------------<
     A further static identifier, self expands to static type of the code it appears within, completing the ways code may want to refer to the type it is declared in.

         self expands to the static type of the code it is declared within. In value types, this is always the same as Self. In reference types, it refers to the declaring type. self will offer a literal textual replacement just like #file, etc.
>----------------------------<

Then it was transformed(in your [Draft]) to 'invariant type semantics in all contexts' with main target to use in '->StaticSelf' as requirement in protocols. Now, that proposal 'Will not be proposed'.

But what about the initial main idea?

So, I suggest to discuss if we want/need that self(or StaticSelf) in meaning '*placeholder for the type name where it is defined*'

I really think that such self can increase readability, clarity of code and could protect for some kind of errors(when you typed the wrong name of type or copy-pasted without change the name of the type).

I.e. as I understand the initial manin idea, the self could be used only inside class/struct/enum, not in protocols. So we can refer the same type not by concrete name, but by self(or StaticSelf or another chosen name)

What do you think?

···

On 18.05.2016 20:37, Erica Sadun via swift-evolution wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's
resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution >> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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.

···

Sent from my iPad

On May 18, 2016, at 9:57 PM, Patrick Smith <pgwsmith@gmail.com> wrote:

On 19 May 2016, at 3:37 AM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

It strikes me that in a sense, the proposed StaticSelf is basically what '_' does

By specifying StaticSelf we are trying to tell the compiler to ignore what is going on that that precise location to revert to something expressed 'earlier'. This is somehwat anologous to what we do when we use '_' in signatures: we escape the specifics of the current location, ony in that case there is no prior context to refer to, so the parameter winds up having no particular name.

If you try to read the examples with this interpretation: "nothing specific from this location" then you can see how you wind up 'inheriting' the previously expressed static type of self. So I would seriously consider using _ instead of adding StaticSelf.

This proposal is no longer being pursued. Please have a look at the solution that has been identified if you're interested.

···

Sent from my iPad

On May 19, 2016, at 3:14 AM, L. Mihalkovic <laurent.mihalkovic@gmail.com> wrote:

Regards
(From mobile)

On May 18, 2016, at 7:37 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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@anandabits.com> wrote:

Sent from my iPad

On May 18, 2016, at 9:57 PM, Patrick Smith <pgwsmith@gmail.com <mailto:pgwsmith@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@swift.org <mailto:swift-evolution@swift.org>> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

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.

Joe Groff has suggested allowing control over inheritance of conformance at the point where conformance is declared. That would work how you want. There was quite a bit of discussion about this over the winter. The details of designing it are complicated enough that it probably won't happen any time soon.

···

Sent from my iPad

On May 18, 2016, at 10:14 PM, Patrick Smith <pgwsmith@gmail.com> wrote:

Anyway, nice resolution!

On 19 May 2016, at 1:06 PM, Matthew Johnson <matthew@anandabits.com> wrote:

Sent from my iPad

On May 18, 2016, at 9:57 PM, Patrick Smith <pgwsmith@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@swift.org> wrote:

As a wrap-up of the topic, I've updated our original draft with Nicola S's resolution.

StaticSelf.md · GitHub

-- E

On May 14, 2016, at 8:35 AM, Matthew Johnson via swift-evolution <swift-evolution@swift.org> wrote:

On May 14, 2016, at 12:55 AM, Nicola Salmoria via swift-evolution <swift-evolution@swift.org> wrote:

Matthew Johnson via swift-evolution <swift-evolution@...> 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@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution