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

Adrian Zubarev adrian.zubarev at devandartist.com
Sun Jan 8 04:09:09 CST 2017


There are a few good points made here. It’s an interesting workaround to use type(of:) to get a similar behavior I wanted. My point is, that Current or call it static Self should exist to fill some gaps in our design patterns. Using Self in protocols should not always mean that you shall use the dynamic behavior. Yes on value types the dynamic type is the same conforming type.

// All these protocol do not compile
// This is not real world example, but IMO should be possible
protocol P : class {
     
    associatedtype StaticSelf : AnyObject
    func castOrTrap<T : StaticSelf>() -> T
}

protocol P : class {
     
    associatedtype StaticSelf : Self
    func castOrTrap<T : StaticSelf>() -> T
}

protocol P : class {
     
    func castOrTrap<T : Self>() -> T
}
The absence of static Self does hurt the flexibility of the language to design a specific but yet clear behavior. The P protocol is meant to have a static Self, where conforming to it would result in a shorthand version using StaticSelf/Current or the conforming type:

class A : P {
     
    func castOrTrap<T : Current>() -> T { … }
    // or
    func castOrTrap<T : A>() -> T { … }
}
I cannot think of a possible workaround here except of defining that method on the base type itself. If other not derived classes need to have this pattern, I cannot create an ancestor protocol as I might would like.

The forced required init might be a good workaround for the issue from some previous post, but is it really what we always wanted? This restriction lives only on the non-final classes because Self there is dynamic, which feels kinda inconsistent. As I already mentioned, I’d be happy if we could drop the restriction on the conforming type to allow the user to decide if we want to follow the contract of using self (lowercased) and type(of: self) or could simply override Self with ContainingTypeName. That would solve that issue, if I’m not totally missing here something.

But the issue from above remans unsolved. Furthermore, does dynamic Self make any sense on non-final classes as a parameter type? Can anyone show me a plausible code snippet for that?

Sure my arguments are more like might, want etc. and not that much of a weight, but that’s my honest opinion that some restrictions makes the language less flexible as it could be.

Self is exactly like .Type which might also be magically .Protocol. The static and dynamic behaviors are baked into one place. :/



-- 
Adrian Zubarev
Sent with Airmail

Am 8. Januar 2017 um 00:51:32, Xiaodi Wu via swift-evolution (swift-evolution at swift.org) schrieb:

On Sat, Jan 7, 2017 at 5:33 PM, Braeden Profile <jhaezhyr12 at gmail.com> wrote:

Of course, I would love being able to use an initializer setup, but there are serious bugs in the implementation.

protocol Clonable
{
init(other: Self)
}

extension Clonable
{
func clone() -> Self
{ return type(of: self).init(other: self) }
}


class Base: Clonable
{
var x: Int

init(x: Int)
{ self.x = x }

required init(other: Base)
{ self.x = other.x }
}

class Derived: Base
{
var y: String

init(x: Int, y: String)
{
self.y = y
super.init(x: x)
}

// Should be required by the Clonable protocol, but it isn't.
required init(other: Derived)
{
self.y = other.y
super.init(other: other)
}

// Required because it was `required` in Base.  Even a `Derived` calls this initializer to clone, which is wrong.  Bugs abound.
required init(other: Base)
{ fatalError("init(other:) is wrong.") }
}



let me = Derived(x: 1, y: "food")
let alienClone = me.clone() // "init(other:) is wrong."


Agree. That seems wrong. Great example.

So, is this odd behavior intentional, a bug, or a design deficiency?  I would think that when a protocol has a method or initializer has `Self` parameters—like in Clonable—every subclass would be required to implement its own specialized version (much like a required initializer).  That would be a special case of the protocol system, though.

As it sits, even fixing the calling behavior of my example leaves us with the problem of subclasses inheriting inapplicable required initializers from superclasses that actually don’t make any sense.

Does this deserve its own thread?

Dunno, maybe best to have its own thread. It's not mentioned as part of SE-0068, but IMO a complete design that respects the spirit of that proposal *should* involve allowing you to write:

```
class Base : Clonable {
  required init(other: Self) { ... }
}

class Derived : Base {
  required init(other: Self) { ... }
}
```


_______________________________________________
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/20170108/c8d6ce4b/attachment.html>


More information about the swift-evolution mailing list