[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