[swift-evolution] [RFC] #Self

Thorsten Seitz tseitz42 at icloud.com
Thu May 12 13:10:55 CDT 2016


I absolutely share Matthew’s view of #Self (or whatever it ends up being called) and think this is really needed.

On the other hand I don’t think that a simple placeholder for the defining type is necessary as proposed by Vladimir, especially not if it is the third variant of something Self-like. 

-Thorsten


> Am 12.05.2016 um 17:36 schrieb Matthew Johnson via swift-evolution <swift-evolution at swift.org>:
> 
>> 
>> On May 12, 2016, at 10:19 AM, Vladimir.S <svabox at gmail.com <mailto: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 <mailto: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.
> 
> _______________________________________________
> 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/20160512/ca23d82a/attachment.html>


More information about the swift-evolution mailing list