[Draft] Introducing StaticSelf, an Invariant Self


(Thorsten Seitz) #1

Comments inline.

I don't feel that InvariantSelf reflects the fact that class conformed to
protocol with `f()->InvariantSelf` requirement will actually return
'self or some base class'. Especially when this `InvariantSelf` means
`exactly this concrete static type name` inside type declaration.

Probably the better name is BaseType (BaseSelf, ThisType.. #Type ?)

But actually I don't fully understand how this would work in generic functions:

protocol A {
func g()->StaticSelf
}

class B: A {
func g()->StaticSelf {return B()}
}

class C: B {
}

func x(a: A ){
var xx : A = a.g() // will this work? as g returns *some in hierarchy*
print(xx)
}

StaticSelf within the protocol should just be the protocol, i.e. here A

Only for non-protocol types conforming to the protocol StaticSelf becomes equal to the first type where the respective method gets implemented (if we should get abstract classes some day this would mean that for an abstract class conforming to the protocol but not implementing the method a, StaticSelf would still be A).

func z<T: A>(t: T) {
let u = t.g() // will this work?
print(u)
}

This will work as well with static type of `u` being A.

let c = C()
z(c)

This is of course correct because C conforms to A. The static type of `u` within `z` is A (see comment above). This is independent from the dynamic type of the parameter `t` (which has nothing to do with StaticSelf).

x(c)

Again `c` conforms to A, so this is correct.

-Thorsten

I like the way the motivation for this feature has been explained here. Now
that the reasoning behind it is evident, I have to say I'm leaning towards
the "InvariantSelf" name--after all, you describe this feature in the title
as "an invariant self."

Erica Sadun and I have written a proposal are following up the recent
discussion thread "[RFC] #Self” with a proposal to introduce
StaticSelf, an invariant Self.

The recent discussion can be found
here: http://thread.gmane.org/gmane.comp.lang.swift.evolution/16565

The proposal can be found
here: https://github.com/anandabits/swift-evolution/blob/static-self/proposals/NNNN-static-self.md

We look forward to continuing the discussion. We plan to submit a PR
in the near future after incorporating your final feedback.

Thanks,
Matthew

Introducing StaticSelf, an Invariant Self

* Proposal: TBD
* Authors: Matthew Johnson <https://github.com/anandabits>, Erica
Sadun <https://github.com/erica>

* Status: TBD
* Review manager: TBD

Introduction

This proposal introduces a new keyword that provides consistent
invariant type semantics in all contexts.

/The Swift-evolution thread about this topic can be found here: [RFC]
#Self <http://thread.gmane.org/gmane.comp.lang.swift.evolution/16565>/

Motivation

The distinction between covariant and non-covariant type references
come into play when
conforming non-final classes to protocols. Fixing a protocol
requirement to a covarying type
means that a method returning |Self| must be overriden by all
subclasses in order to return
the correct, matching type.

This proposal builds on the covariant construct |Self| accepted
in SE–0068
<https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md>

to introduce an invariant type identifier. It enables protocol
declarations to consistently
refer to a type that is fixed at compile time. This ensures that
subclasses can inherit
protocol implementations without having to re-implement that code at
each level of
inheritance.

Under this proposal, a new identifier keyword is fixed in use /at the
point of protocol conformance/
to the static type of that construct.

class A: MyProtocol|

The invariant |StaticSelf| identifier will always refer to |A|,
unlike |Self|, which is covarying and refers to
the type of the actual instance. Since multiple inheritance for
non-protocol types is disallowed,
this establishes this invariant type identifier with no possibility for
conflict.

Consider the following example, under the current system:

protocol StringCreatable { static func createWithString(s: String) ->

Self } extension NSURL: StringCreatable { // cannot conform because
NSURL is non-final // error: method 'createWithString' in non-final
class 'NSURL' must return `Self` to conform to protocol 'A' }|

Introducing a static, invariant version of |Self| permits the desired
conformance:

protocol StringCreatable { static func createWithString(s: String) ->

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

Additional Utility

The utility of |StaticSelf| is not limited to protocols. A secondary
use enables code to refer to the lexical context’s current type without
explicitly mentioning its name. This provides a useful shortcut when
referencing static type members with especially long names and when
re-purposing code between types.

class StructWithAVeryLongName { static func foo() -> String { // ... }

func bar() { // ... let s = StaticSelf.foo() // } }|

Detailed Design

This proposal introduces |StaticSelf|, a new keyword that may be used
in protocols to refer to the invariant static type of a conforming
construct. |StaticSelf| may also be used in the lexical context of any
type declaration. In such use, the keyword is identical to spelling out
the full name of that type.

Impact on existing code

Being additive, there should be no impact on existing code.

Alternatives considered

The keyword is not fixed at this time. Alternatives that have been
discussed include |StaticType|, |InvariantSelf|, |SelfType|, or |Type|.
The community is welcome to bikeshed on the most clear and concise name
for this keyword.

···

Am 13. Mai 2016 um 09:31 schrieb "Vladimir.S via swift-evolution" <swift-evolution@swift.org>:
On 13.05.2016 4:59, Xiaodi Wu via swift-evolution wrote:
On Thu, May 12, 2016 at 7:49 PM, Matthew Johnson via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

_______________________________________________
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

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


(Matthew Johnson) #2

Comments inline.

I don't feel that InvariantSelf reflects the fact that class conformed to
protocol with `f()->InvariantSelf` requirement will actually return
'self or some base class'. Especially when this `InvariantSelf` means
`exactly this concrete static type name` inside type declaration.

Probably the better name is BaseType (BaseSelf, ThisType.. #Type ?)

But actually I don't fully understand how this would work in generic functions:

protocol A {
func g()->StaticSelf
}

class B: A {
func g()->StaticSelf {return B()}
}

class C: B {
}

func x(a: A ){
var xx : A = a.g() // will this work? as g returns *some in hierarchy*
print(xx)
}

StaticSelf within the protocol should just be the protocol, i.e. here A

Only for non-protocol types conforming to the protocol StaticSelf becomes equal to the first type where the respective method gets implemented (if we should get abstract classes some day this would mean that for an abstract class conforming to the protocol but not implementing the method a, StaticSelf would still be A).

Yes, it is exactly as if you had copy and pasted the signature from the protocol declaration into the conforming type.

···

Sent from my iPad

On May 13, 2016, at 8:37 AM, Thorsten Seitz via swift-evolution <swift-evolution@swift.org> wrote:

Am 13. Mai 2016 um 09:31 schrieb "Vladimir.S via swift-evolution" <swift-evolution@swift.org>:

func z<T: A>(t: T) {
let u = t.g() // will this work?
print(u)
}

This will work as well with static type of `u` being A.

let c = C()
z(c)

This is of course correct because C conforms to A. The static type of `u` within `z` is A (see comment above). This is independent from the dynamic type of the parameter `t` (which has nothing to do with StaticSelf).

x(c)

Again `c` conforms to A, so this is correct.

-Thorsten

On 13.05.2016 4:59, Xiaodi Wu via swift-evolution wrote:
I like the way the motivation for this feature has been explained here. Now
that the reasoning behind it is evident, I have to say I'm leaning towards
the "InvariantSelf" name--after all, you describe this feature in the title
as "an invariant self."

On Thu, May 12, 2016 at 7:49 PM, Matthew Johnson via swift-evolution >>> <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Erica Sadun and I have written a proposal are following up the recent
discussion thread "[RFC] #Self” with a proposal to introduce
StaticSelf, an invariant Self.

The recent discussion can be found
here: http://thread.gmane.org/gmane.comp.lang.swift.evolution/16565

The proposal can be found
here: https://github.com/anandabits/swift-evolution/blob/static-self/proposals/NNNN-static-self.md

We look forward to continuing the discussion. We plan to submit a PR
in the near future after incorporating your final feedback.

Thanks,
Matthew

Introducing StaticSelf, an Invariant Self

* Proposal: TBD
* Authors: Matthew Johnson <https://github.com/anandabits>, Erica
Sadun <https://github.com/erica>
* Status: TBD
* Review manager: TBD

Introduction

This proposal introduces a new keyword that provides consistent
invariant type semantics in all contexts.

/The Swift-evolution thread about this topic can be found here: [RFC]
#Self <http://thread.gmane.org/gmane.comp.lang.swift.evolution/16565>/

Motivation

The distinction between covariant and non-covariant type references
come into play when
conforming non-final classes to protocols. Fixing a protocol
requirement to a covarying type
means that a method returning |Self| must be overriden by all
subclasses in order to return
the correct, matching type.

This proposal builds on the covariant construct |Self| accepted
in SE–0068
<https://github.com/apple/swift-evolution/blob/master/proposals/0068-universal-self.md>
to introduce an invariant type identifier. It enables protocol
declarations to consistently
refer to a type that is fixed at compile time. This ensures that
subclasses can inherit
protocol implementations without having to re-implement that code at
each level of
inheritance.

Under this proposal, a new identifier keyword is fixed in use /at the
point of protocol conformance/
to the static type of that construct.

>class A: MyProtocol|

The invariant |StaticSelf| identifier will always refer to |A|,
unlike |Self|, which is covarying and refers to
the type of the actual instance. Since multiple inheritance for
non-protocol types is disallowed,
this establishes this invariant type identifier with no possibility for
conflict.

Consider the following example, under the current system:

>protocol StringCreatable { static func createWithString(s: String) ->
Self } extension NSURL: StringCreatable { // cannot conform because
NSURL is non-final // error: method 'createWithString' in non-final
class 'NSURL' must return `Self` to conform to protocol 'A' }|

Introducing a static, invariant version of |Self| permits the desired
conformance:

>protocol StringCreatable { static func createWithString(s: String) ->
StaticSelf } 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 {
// ... } }|

Additional Utility

The utility of |StaticSelf| is not limited to protocols. A secondary
use enables code to refer to the lexical context’s current type without
explicitly mentioning its name. This provides a useful shortcut when
referencing static type members with especially long names and when
re-purposing code between types.

>class StructWithAVeryLongName { static func foo() -> String { // ... }
func bar() { // ... let s = StaticSelf.foo() // } }|

Detailed Design

This proposal introduces |StaticSelf|, a new keyword that may be used
in protocols to refer to the invariant static type of a conforming
construct. |StaticSelf| may also be used in the lexical context of any
type declaration. In such use, the keyword is identical to spelling out
the full name of that type.

Impact on existing code

Being additive, there should be no impact on existing code.

Alternatives considered

The keyword is not fixed at this time. Alternatives that have been
discussed include |StaticType|, |InvariantSelf|, |SelfType|, or |Type|.
The community is welcome to bikeshed on the most clear and concise name
for this keyword.

_______________________________________________
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

_______________________________________________
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


(Vladimir) #3

After all these discussions, and after I found out *for myself* what does the proposed `->StaticSelf` in protocol mean, I'm giving my strong +1 to this proposal. This will be a good and useful in some cases language feature, that will add flexibility to Swift.

Also it will help to make the code clean and reduce copy-paste errors or requirements to re-type concrete type name inside its declaration.
It's so naturally to say 'this concrete type name' i.e. `StaticSelf` *inside* the type declaration instead of having its name everywhere. Especially, if name of the declaring type has common elements with another class like MyVeryGoodClass.f() and MyVeryBadClass.f() so I need to carefully read name of which class used.

But the question regarding the name of StaticSelf is still here...
I feel like something that will be resolved at compile-time should have `#` before its name.. Probably the best name for me is #BaseType (or #TypeBase, or #This #BaseThis #ThisBase)... as having StaticSelf, we IMO should have DynamicSelf instead of current `Self`.. Even probably the initially proposed #Self is better for me.