[swift-evolution] [RFC] #Self

Matthew Johnson matthew at anandabits.com
Thu May 12 10:36:25 CDT 2016


> On May 12, 2016, at 10:19 AM, Vladimir.S <svabox at gmail.com> wrote:
> 
> Inline
> 
> On 11.05.2016 21:31, Matthew Johnson wrote:
>> 
>> 
>> Sent from my iPad
>> 
>>> On May 11, 2016, at 11:43 AM, Vladimir.S <svabox at gmail.com> wrote:
>>> 
>>> Well, I believe I understand now what *you mean* under #Self. OK.
>>> Thank you for clarifications. In my terminology 'this' could be called
>>> BaseSelf. Your "thing" just can not be called #Self. IMO in initial
>>> proposal #Self means not more than placeholder for the concrete type
>>> name(inside type declaration or inside protocol).
>>> 
>>> You propose just something different, more advanced than initial
>>> #Self, you propose not some static "thing" but extended behavior if
>>> #Self is a return type of protocol requirement.
>>> 
>>> I strictly against to couple the initial proposal of #Self and your
>>> proposal for extended features (for protocol conformance of
>>> `->#Self`). Please be clear and obvious regarding the name of that
>>> feature. I really think the behavior you propose can not be called
>>> #Self(or Type)
>>> 
>>> What I suggest: de-couple these proposals to:
>>> 
>>> a) initial proposal of #Self as placeholder for concrete type name.
>>> Choose the name for it. Probably StaticSelf, or Type, or somehting
>>> else
>>> 
>>> b) your proposal for BaseSelf feature. I'll definitely support it with
>>> just name changed to clearly reflect its propose.
>> 
>> I don't believe the initial proposal stated how it would behave in a
>> protocol.  However I do believe the feature I am talking about covers
>> all of the use cases Erica had in mind while also providing useful
>> semantics when used in a protocol requirement.  Erica, please correct me
>> if I'm wrong.
> 
> Well.. Yes, I also don't see statements regarding how `->#Self` would behave in a protocol in initial proposal. *This is why* I suggest to de-couple what *was* in initial proposall, and what you are suggesting as *addition* to the proposal.
> 
> Again. I fully support the behavior you suggest, but I strongly feel like 'that' #Self can't be named #Self, as some derived(from conformed class) class X does not return 'self'. It return 'self or one of base classes'. Let's call it 'SelfOrBase' for now.
> 
> What we have now:
> 
> protocol A {
>  func f1() -> Self
>  func f2(s: Self)
> }
> 
> struct S: A {
>  func f1() -> S/*#Self*/ {return self}
>  func f2(s: S/*#Self*/) {}
> }
> 
> class C: A {
>  func f1() -> Self /*only Self, can't write C here*/ {return self}
>  func f2(s: C/*#Self*/) {}
> }
> 
> final class FC: A {
>  func f1() -> Self /*can write FC here, ==#Self*/ {return self}
>  func f2(s: FC/*#Self*/) {}
> }
> 
> I believe, for clarity, after we introduce #Self(or whatever name it will have) we need to require `#Self` in protocol as type of method parameter:
> 
> protocol A {
>  func f1() -> Self // this could be Self and could be #Self

What in the original proposal makes you think Self and #Self would be interchangeable here?  Again, the proposal was for #Self to be invariant.  Self is covariant.  Those are very different things.  

The semantics in the original proposal were unspecified with regards to protocols.  I am simply extending it to retain the invariant semantics that the proposal *did* specified when it is used as a protocol requirement.

>  func f2(s: #Self) // this always will be #Self in implementation
> 
>  // this should not be allowed any more
>  //func f2(s: Self)
> }
> 
> struct S: A {
>  // as you see we'd have `->#Self` in implementation
>  func f1() -> #Self {return self}
>  func f2(s: #Self) {}
> }
> 
> class C: A {
>  func f1() -> Self /*only Self, can't write C here*/ {return self}
>  func f2(s: #Self) {}
> }
> 
> final class FC: A {
>  func f1() -> Self /*can write FC here, ==#Self*/ {return self}
>  func f2(s: #Self) {}
> }
> 
> The above is what *I think* was in initial proposal regarding #Self.
> 
> About your suggestion. I just trying to understand it in details.
> Please point me where I understand your suggestion incorrectly.
> 
> Do you think about such start points? :
> 
> class Base {
>  func f() -> Base  {return Base()}
> }
> 
> class Derived1: Base {
>  override func f() -> Derived1  {return Derived1()}
> }
> 
> class Derived2: Base {
>  override func f() -> Derived2  {return Derived2()}
> }
> 
> If so, do you want to introduce such a protocol:
> 
> protocol A {
>    func f() -> #Self
> }
> 
> and then conforms Base class to it? :
> 
> extension Base : A {}
> 
> To be able to use :
> 
> let a : A = Derived2()
> let some = a.f() // you expect some.dynamicType is Derived2

The protocol specifies #Self (or Type, which we are not calling it).  It is conformed to by Base.  This means the return type of `f` is only guaranteed to be Base.  It *may* be Derived2, but it need not be.  Obviously given this example we know it is Derived2 because we can see the implementation but the type system does not.  This means `some` has type of Base.  Its dynamicsType *may* be Derived2 (and again in this case we can see that it will be) but that is not guaranteed by the type system.

> 
> I understand correctly?
> 
> At this point you say that all is OK and #Self works as expected, each class returns really #Self and protocol conformation applied.
> But I believe this is not true. We *can* have this:
> 
> class Derived3: Base {
>  // no override for f(). will be used from base class
> }
> 
> Now. What does Derived3().f() returns? It returns instance of Base.
> Is #Self for Derived3 equals to `Base` ? No.
> Does Derived3 conforms to protocol `A` in this case? No.
> But it *must* conform as you think you can conform its base class.
> This is the problem I see.

What you are missing is that #Self is not the same as Self.  It is invariant.  A was originally conformed to by Base.  The requirement is invariant and becomes fixed by the conformance declaration.  In this case it is fixed to Base.

You have not explained why you think #Self should be invariant when used in a type context and covariant when used in a protocol context.  This expectation is what is confusing you.  If you really what that semantic I think the burden is on you to make the case that we need something with this mixed semantic.  I think it is much more confusing than having separate constructs for covariant and invariant semantics.

> 
> This is why I think it must be something separate from #Self, like SelfOrBase - in this case, protocol will looks like:
> 
> protocol A {
>    func f() -> SelfOrBase
> }
> 
> then you can conform Base to A,
> 
> extension Base : A {}
> 
> and Derived3 fully conforms to this protocol(it returns instance of base class), all is OK and all is consistent.
> 
> If I'm wrong somewhere please point me with clarification. Thank you.

Again, what you are calling SelfOrBase is *exactly* what I am calling Type (or #Self).  It has consistent invariant semantics in all contexts.

> 
>> 
>> You want to make the semantics of #Self / Type be covariant when used in
>> a protocol requirement.  This makes no sense to me as it is explicitly
>> *not* covariant when used within a class declaration.  We already have a
>> covariant construct (Self) and the proposal is to introduce an invariant
>> construct (#Self or Type).  The invariant semantic should be consistent
>> regardless of whether it is used in a protocol requirement or a type
>> declaration.
>> 
>> IMO BaseSelf is a poor choice of name for something that is supposed to
>> be valid syntax in value types as well as classes.
>> 
>>> 
>>>> On 11.05.2016 18:58, Matthew Johnson wrote: 'f' would return E for
>>>> E, F and G.  Because the conformance is declared by E the
>>>> requirement to return #Self is fixed as an invariant requirement to
>>>> return E for all potential subclasses.
>>>> 
>>>>>> 
>>>>>> Probably you(we) need another proposal, like BaseSelf (or
>>>>>> SuperSelf) that means "this class or any its base class", then I
>>>>>> understand how such a `f()->BaseSelf` protocol requirement can
>>>>>> be applied to E class and also be true for F&G classes (as f()
>>>>>> inherited from base class will return instance of E which is
>>>>>> base for both).
>>>> This is exactly what #Self (or Type) does.  The behavior you have
>>>> been describing is the behavior of Self which already exists.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160512/59b25f46/attachment.html>


More information about the swift-evolution mailing list