[Pitch] Tweak `Self` and introduce `Current`


(Adrian Zubarev) #1

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:

Refer to the current type, or refer to the dynamic type for non-final classes inside containing type (SE–0068 - not yet implemented).
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.

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

Current can only refer to the current containing type.

On classes Self has always the contract of returning self.

Self could be discouraged in favor of Current on value types, as a shorthand to refer to the containing type.

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


(thislooksfun) #2

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@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:

Refer to the current type, or refer to the dynamic type for non-final classes inside containing type (SE–0068 - not yet implemented).
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.

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

Current can only refer to the current containing type.

On classes Self has always the contract of returning self.

Self could be discouraged in favor of Current on value types, as a shorthand to refer to the containing type.

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


(Xiaodi Wu) #3

`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@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@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@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


(Xiaodi Wu) #4

`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.'"

I should add, I say this because even though we have revisited proposals
after their acceptance on discovery of an unforeseen problem, IMO it's
highly disagreeable to try to reconsider an accepted proposal before it's
even been implemented on the basis of disagreeing with the conclusion of a
discussion that was already considered previously.

···

On Sat, Jan 7, 2017 at 11:34 AM, Xiaodi Wu <xiaodi.wu@gmail.com> wrote:

On Sat, Jan 7, 2017 at 11:14 AM, thislooksfun via swift-evolution < > swift-evolution@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@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@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


(Adrian Zubarev) #5

As a naming contrast, one could make Self like the proposed Current, and rename the current Self to DynamicSelf or something similar.

Most protocols that are meant for value types in first place should use Self aka. Current as the return type.

···

--
Adrian Zubarev
Sent with Airmail

Am 7. Januar 2017 um 11:38:55, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

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:

Refer to the current type, or refer to the dynamic type for non-final classes inside containing type (SE–0068 - not yet implemented).
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.

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

Current can only refer to the current containing type.

On classes Self has always the contract of returning self.

Self could be discouraged in favor of Current on value types, as a shorthand to refer to the containing type.

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


(Adrian Zubarev) #6

True, but there are a few edge cases where Self simply does not work.

On classes the return type has the contract to return self.
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. 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 { ... }
}

···

--
Adrian Zubarev
Sent with Airmail

Am 7. Januar 2017 um 18:34:38, Xiaodi Wu (xiaodi.wu@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@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@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:

Refer to the current type, or refer to the dynamic type for non-final classes inside containing type (SE–0068 - not yet implemented).
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.

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

Current can only refer to the current containing type.

On classes Self has always the contract of returning self.

Self could be discouraged in favor of Current on value types, as a shorthand to refer to the containing type.

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@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


(Anton Zhilin) #7

I have no problem with the syntax proposed in SE-0068.
The rationale briefly mentions that dynamic Self will be used anywhere
inside the class body. I think that the possibilities that open with this
decision should be mentioned in the proposal.

We can say that non-final classes, apart from that they can also have
instances of themselves, behave very much like protocol existentials. They
couple requirements and “default implementation”. Like existential
protocols, they can’t have associated types. SE-0068 expands abilities of
classes and allows their requirements to contain Self. “Default”
implementation is mandatory, as usual. Example of code that will be
possible:

// Compiles now

protocol P {
    init()
    func foo(_ x: Self) -> Self
}

extension P {
    func foo(_ x: Self) -> Self {
        print(Self.self)
        return Self.init()
    }
}

struct S : P { }

let x = S()
_ = x.foo(x) // prints "S"

// Will compile after implementation of SE-0068

class A {
    required init() {
    }

    func foo(_ x: Self) -> Self {
        print(Self.self)
        return Self.init()
    }
}

class B : A { }

let y = B()
_ = y.foo(y) // prints "B"

I find that consistent enough. The annoyance is that we don’t and won’t
have the ability to implement protocol requirements with Self in classes
without Self. This is an error:

protocol P {
    func foo() -> Self
}

class A {
    func foo() -> A { ... }
}

class B : A {
    override func foo() -> B { ... }
}

The fact that we can’t get rid of Self in implementation is an abstraction
leak.

As a side note, seeing Self.self, I really look forward to refactoring Self,
self, Type, Protocol and MemoryLayout.


(Xiaodi Wu) #8

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>.

···

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

--
Adrian Zubarev
Sent with Airmail

Am 7. Januar 2017 um 18:34:38, Xiaodi Wu (xiaodi.wu@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@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@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@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


(Rien) #9

Is there any interest in a proposal to introduce a named defer statement that can be cancelled?

Lately I find myself writing this kind of code:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
    
    var file = fopen("MyFile.txt", "r")
    
    var closeFile = true

    defer { if closeFile { fclose(file) } }
    
    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }
    
    if fileDataOutOfDate(file) { return nil }
    
    // Prevent the deferred handler from closing the file
    closeFile = false

    return file
}

Which imo would be much cleaner if we were able to write:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
    
    var file = fopen("MyFile.txt", "r")
    
    CLOSE_FILE: defer { fclose(file) }
    
    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }
    
    if fileDataOutOfDate(file) { return nil }
    
    // Prevent the deferred handler from closing the file
    cancel CLOSE_FILE

    return file
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl


(Slava Pestov) #10

True, but there are a few edge cases where Self simply does not work.

On classes the return type has the contract to return self.
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. 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.

Actually it is possible to construct values of type “Self” inside a class that are not “self”. type(of: self) returns a metatype of type Self.Type, which you can then invoke required initializers on to get a new instance of type ‘Self’.

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

Using the dynamic “Self” like that would not be allowed even with SE-0068, because of implementation constraints — only a static class name can appear in a superclass constraint like that, not a type parameter.

Slava

···

On Jan 7, 2017, at 10:02 AM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

--
Adrian Zubarev
Sent with Airmail

Am 7. Januar 2017 um 18:34:38, Xiaodi Wu (xiaodi.wu@gmail.com <mailto:xiaodi.wu@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@swift.org <mailto:swift-evolution@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@swift.org <mailto:swift-evolution@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:

Refer to the current type, or refer to the dynamic type for non-final classes inside containing type (SE–0068 - not yet implemented).
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.

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

Current can only refer to the current containing type.

On classes Self has always the contract of returning self.

Self could be discouraged in favor of Current on value types, as a shorthand to refer to the containing type.

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@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


(Xiaodi Wu) #11

-1. Much of the utility of `defer` is that it's guaranteed to execute. As
you demonstrate, it's possible to capture a flag and choose not to do
something within the defer block itself. We don't need sugar for that.

···

On Sat, Jan 7, 2017 at 12:20 PM, Rien via swift-evolution < swift-evolution@swift.org> wrote:

Is there any interest in a proposal to introduce a named defer statement
that can be cancelled?

Lately I find myself writing this kind of code:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    var closeFile = true

    defer { if closeFile { fclose(file) } }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    closeFile = false

    return file
}

Which imo would be much cleaner if we were able to write:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    CLOSE_FILE: defer { fclose(file) }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    cancel CLOSE_FILE

    return file
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

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


(D. Felipe Torres) #12

Any cancelable defer addition we could come up with will need a flag/state
to indicate so, which won't be that much different from what you wrote.

Having said that, for your example may I suggest this approach instead:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    guard fileIsValid(file)

        && fileContainsData(file, kind)

        && !fileDataOutOfDate(file) else {

        fclose(file)

    }

    return file

}

···

On Sat, Jan 7, 2017 at 7:20 PM, Rien via swift-evolution < swift-evolution@swift.org> wrote:

Is there any interest in a proposal to introduce a named defer statement
that can be cancelled?

Lately I find myself writing this kind of code:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    var closeFile = true

    defer { if closeFile { fclose(file) } }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    closeFile = false

    return file
}

Which imo would be much cleaner if we were able to write:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    CLOSE_FILE: defer { fclose(file) }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    cancel CLOSE_FILE

    return file
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl

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

--
++++++++++++++++++++++++++
Diego Torres.
Phone (Mobile Germany): +49 157 30070985
Phone (Landline Chile): +56 2 29790978
Web: dtorres.me


(Derrick Ho) #13

Its probably better to use a Boolean variable close_file to "cancel" it,
rather than change the entire language.

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")
    var close_file = true
    defer { if close_file { fclose(file) }} // <--

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
   close_file = false // <--

    return file
}

···

On Sat, Jan 7, 2017 at 1:20 PM Rien via swift-evolution < swift-evolution@swift.org> wrote:

Is there any interest in a proposal to introduce a named defer statement
that can be cancelled?

Lately I find myself writing this kind of code:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    var closeFile = true

    defer { if closeFile { fclose(file) } }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    closeFile = false

    return file
}

Which imo would be much cleaner if we were able to write:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {

    var file = fopen("MyFile.txt", "r")

    CLOSE_FILE: defer { fclose(file) }

    if fileIsNotValid(file) { return nil }

    if fileDoesNotContainsData(file, kind) { return nil }

    if fileDataOutOfDate(file) { return nil }

    // Prevent the deferred handler from closing the file
    cancel CLOSE_FILE

    return file
}

Regards,
Rien

Site: http://balancingrock.nl
Blog: http://swiftrien.blogspot.com
Github: http://github.com/Swiftrien
Project: http://swiftfire.nl
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #14

Or:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
    var file = fopen("MyFile.txt", "r”)

    do {
        if fileIsNotValid(file) { throw MyError.fileIsNotValid }

        if fileDoesNotContainsData(file, kind) { throw MyError.doesNotContainData }
    
        if fileDataOutOfDate(file) { throw MyError.dataOutOfDate }

        return file
    } catch {
        fclose(file)
    }
}

Charles

···

On Jan 7, 2017, at 12:40 PM, D. Felipe Torres via swift-evolution <swift-evolution@swift.org> wrote:

Any cancelable defer addition we could come up with will need a flag/state to indicate so, which won't be that much different from what you wrote.

Having said that, for your example may I suggest this approach instead:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
    
    var file = fopen("MyFile.txt", "r")
    guard fileIsValid(file)
        && fileContainsData(file, kind)
        && !fileDataOutOfDate(file) else {
        fclose(file)
    }
    
    return file
}


(Jay) #15

A closure assigned to a variable is already a named handler…
Make it optional and it’s now a cancelable named handler…
Put it in a defer block and you have exactly what you want, a cancelable
named defer handler…

func doSomething(input: String) -> String? {

    // Create a cancelable named defer handler
    var namedDeferHandler: (() -> Void)? = { print("It didn't work,
but I've taken care of any cleaning up.") }
    defer { namedDeferHandler?() }

    // Check the input
    if input == "" { return nil }

    // Cancel the named defer handler
    namedDeferHandler = nil

    return "It worked!"
}

It’s very flexible, you can have multiple handlers and cancel only some of
them, include some non-optional code in the defer block itself, etc.

···

On Sat, 7 Jan 2017 at 18:45 Charles Srstka via swift-evolution < swift-evolution@swift.org> wrote:

> On Jan 7, 2017, at 12:40 PM, D. Felipe Torres via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Any cancelable defer addition we could come up with will need a
flag/state to indicate so, which won't be that much different from what you
wrote.
>
> Having said that, for your example may I suggest this approach instead:
>
> func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
>
> var file = fopen("MyFile.txt", "r")
> guard fileIsValid(file)
> && fileContainsData(file, kind)
> && !fileDataOutOfDate(file) else {
> fclose(file)
> }
>
> return file
> }

Or:

func openFile(kind: String) -> UnsafeMutablePointer<FILE>? {
    var file = fopen("MyFile.txt", "r”)

    do {
        if fileIsNotValid(file) { throw MyError.fileIsNotValid }

        if fileDoesNotContainsData(file, kind) { throw
MyError.doesNotContainData }

        if fileDataOutOfDate(file) { throw MyError.dataOutOfDate }

        return file
    } catch {
        fclose(file)
    }
}

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