[swift-evolution] [Pitch] Tweak `Self` and introduce `Current`

Xiaodi Wu xiaodi.wu at gmail.com
Sat Jan 7 12:34:06 CST 2017


On Sat, Jan 7, 2017 at 12:02 PM, Adrian Zubarev <
adrian.zubarev at devandartist.com> wrote:

> True, but there are a few edge cases where Self simply does not work.
>
>    1. On classes the return type has the contract to return self.
>    2. Even if we get SE–0068, will Self work in generic context like
>    showed in OP with Current?
>
> #1 Using value semantics on classes means that the returned instance is a
> new instance different from self.
>
> Assume this small protocol:
>
> // Follow value semantics == immutability
> protocol Cloned : class {
>       func cloned() -> Self
> }
>
> class A : Cloned {
>      func cloned() -> Self {
>            return /* copy of self */ <— ERROR
>      }
> }
>
> One could workaround the problem and use final, but what if the class
> meant to be subtypeable? Self simply does not work in this scenario.
>

It works exactly as it should in this scenario. If A isn't final, then by
definition it's impossible for A to make a copy of type Self. I see,
however, what you mean, which is that you wish you could write a protocol
requirement for a function that returns #Self.

The only workaround here would be associatedtype T as the return type
> instead of Self, but is this really what we wanted to describe in our
> protocol. T could be anything else and we cannot constrain it like: associatedtype
> T : Self.
>
> Either a static Self is needed or we need to remove the restriction that
> on the conforming non-final class we cannot overrider Self with the
> TypeName, like on any of it’s subtype.
>
> \2# On non-final classes from the generic context something like this
> following snippet seems to be odd if Self is dynamic.
>
> // What is the constraint here? `Self` is dynamic.
> // I might want to cast to `A` where `Self` in this case would be e.g.`NSObject`
> // but the dynamic type of the current instance is `B`.
> // The relationship might look like this: B : A : NSObject
> // `Self` would refer to `B`, but `A` is not a subclass of `B`.
> extension AReallyLongNonFinalClassName {
>     func casted<T : Self>() -> T { ... }
> }

// In contrast the proposed static `Self` as `Current`
> extension AReallyLongNonFinalClassName {
>     func casted<T : Current>() -> T { ... }
> }
>
>
As indicated in the accepted proposal, the intention is that you would
write <T : AReallyLongNonFinalClassName>.


> --
> Adrian Zubarev
> Sent with Airmail
>
> Am 7. Januar 2017 um 18:34:38, Xiaodi Wu (xiaodi.wu at gmail.com) schrieb:
>
> `Self` _always_ refers to the dynamic type of `self`. It just happens to
> be that in the case of structs the dynamic type is the same as the static
> type. The idea of having a shorthand for the containing type (spelled #Self
> or StaticSelf) was discussed during consideration of SE-0068. The accepted
> version of the proposal rejects that idea, having adopted the position that
> "You will continue to specify full type names for any other use. Joe Groff
> writes, 'I don't think it's all that onerous to have to write ClassName.foo
> if that's really what you specifically mean.'"
>
>
> On Sat, Jan 7, 2017 at 11:14 AM, thislooksfun via swift-evolution <
> swift-evolution at swift.org> wrote:
>
>> I like this idea, however, if I understand the proposal correctly, I
>> think that the naming would make more sense the other way around. `Self`
>> is, at least in my head, tied directly and statically to the enclosing
>> type, where as `Current` sounds more dynamic, and could change from
>> place-to-place.
>>
>> -thislooksfun (tlf)
>>
>> On Jan 7, 2017, at 4:38 AM, Adrian Zubarev via swift-evolution <
>> swift-evolution at swift.org> wrote:
>>
>> Hi Swift community,
>>
>> I’d like to talk to about current Self keyword. If I’m not totally
>> mistaken then the current Self has a few meanings:
>>
>>    1. Refer to the *current* type, or refer to the dynamic type for
>>    non-final classes inside containing type (SE–0068 - not yet implemented).
>>    2. For non-final class types use Self as return type on the
>>    conforming super type (or *return an instance of receiver Self*).
>>
>> Let me visualize the behaviors quickly in some short code snippet:
>>
>> protocol Foo {
>>     func foo(_ f: Self) -> Self
>> }
>>
>> class A : Foo {
>>     // forced to use `A` as parameter type and `Self` as return type
>>     func foo(_ f: A) -> Self { return self }
>>     // Returning `A()` would cause an error: Cannot convert return expression of type 'A' to return type 'Self'
>>     func bar() -> A { return A() /* or self */ }
>>     func zoo() -> Self { return /* only */ self }
>> }
>>
>> class B : A {
>>     // Both is fine `B` or `Self` as the return type
>>     // If `B` is used you can return a different instance like `B()`
>>     // `Self` does only allow `self` to be used here
>>     override func foo(_ f: A) -> B { return self }
>> }
>>
>> struct D : Foo {
>>     // No `Self` allowed here at all
>>     func foo(_ f: D) -> D { return self /* or D() */ }
>> }
>>
>> The behavior of Self is a little magical, because it sometimes refers to
>> the *current* type it is used in, or it has a contract of using self.
>> ------------------------------
>>
>> I propose of introducing a new keyword called Current to solve a few
>> problems here.
>>
>>    1.
>>
>>    Self on parameter types would be disallowed for protocol members,
>>    because conformances to that protocol already disallow that (see A above).
>>    Instead one would use Current and get the correct meaning.
>>
>>    protocol Boo {
>>        func boo(_ b: Current) -> Self
>>    }
>>
>>    procotol Loo {
>>        func loo() -> Current
>>    }
>>
>>    class X : Boo, Loo {
>>        func boo(_ b: X) -> Self { return self }
>>        func loo() -> X { return self /* or X() */ }
>>    }
>>
>>    final class Y : Boo {
>>        func boo(_ b: X) -> Y { return self /* or Y */ }
>>    }
>>
>>    2.
>>
>>    Using Self inside the containing type would always mean as one would
>>    refer to the dynamic type, like the magical syntax function type(of:)
>>    does.
>>    3.
>>
>>    Current can only refer to the current containing type.
>>    4.
>>
>>    On classes Self has always the contract of returning self.
>>    5.
>>
>>    Self could be discouraged in favor of Current on value types, as a
>>    shorthand to refer to the containing type.
>>    6.
>>
>>    Generics could benefit from Current too:
>>
>>    extension AReallyLongNonFinalClassName {
>>        func casted<T : Current>() -> T { ... }
>>    }
>>    // `Self` wouldn't here, because it would refer to the dynamic type
>>
>>
>> #1 Would affect a lot of protocols which implies that it would affect ABI.
>> ------------------------------
>>
>> These are the first ideas I had in my mind. We can polish it further if
>> it receives positive and constructive feedback.
>>
>> Best regards,
>>
>>
>>
>> --
>> Adrian Zubarev
>> Sent with Airmail
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>>
>> _______________________________________________
>> swift-evolution mailing list
>> swift-evolution at swift.org
>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170107/8be9eafb/attachment.html>


More information about the swift-evolution mailing list