> Are there reasons that prevent using `Self` as a synonym for an instance's 
> type name?
> 
> Consider:
> 
> struct MyStruct {
>     static func foo() { print("foo") }
>     func bar() {
>         MyStruct.foo() // works
>         self.dynamicType.foo() // works
>         Self.foo() // error
>     }
> }
> 
> Obviously, you can always name a type directly or use `self.dynamicType` but
> neither solution does any favors for readability. Both approaches obscure 
> intent, 
> especially as type names grow large: `MyExtremelyLargeTypeName.staticMember`,
> for example. Plus, as Kevin B pointed out to me,  
> `self.dynamicType.classMember`  
> and `TypeName.classMember` may not be synonyms in class types with non-final 
> members.
> 
> I'd like to see `Self.staticMember` introduced as a synonym for 
> `TypeName.staticMember`.
> 
> Thoughts?

I'm kind of struggling with how best to design this. Here's the most coherent 
design I've been able to come up with so far.

* Adopt the proposal to no longer require `.self` on types to get the type 
instance. Using a type name in expression context gives you the type instance.
* Every variable `foo` has a typealias attached to it, `foo.Self`. This is the 
static (compile-time declared or inferred) type of that instance. You can use 
it anywhere you can use a type name, including in declarations. If it's used in 
an expression, it becomes the type instance of the variable's static type.
* Every variable `foo` has a special typealias attached to it, 
`foo.DynamicSelf`. This is the dynamic (runtime assigned) type of that 
instance. In theory you can use it anywhere you can use a type name, though in 
practice there are probably significant limitations. If it's used an 
expression, it become the type instance of the variable's dynamic type.
* A bare `Self` or `DynamicSelf` is a shorthand for `self.Self` or 
`self.DynamicSelf`.

`DynamicSelf` subsumes the roles of both the old `Self` and `dynamicType`. 
`Self` is both an alias for the declared type and a way to get its type 
instance.

This gives us a number of new abilities:

        Self.classMember()                              // Instead of 
ReallyLongClassName.classMember()
        foo.Self.classMember()                  // Likewise, but for a 
different variable
        
        let self2: Self                                 // Match the static 
type of self
        let foo2: foo.Self                              // Match the static 
type of a different variable
        
        DynamicSelf.classMember()               // Instead of 
self.dynamicType.classMember()
        foo.DynamicSelf.classMember()   // Likewise

        let self3: DynamicSelf                  // Match the dynamic type of 
self
        let foo3: foo.DynamicSelf               // Match the dynamic type of a 
different variable
        // (Those would probably require certain restrictions, like the base 
variable has to be 
        // immutable and the assignment has to come from a function returning a 
DynamicSelf
        // derived from `self`/`foo`.)
        
        // Make promises about matching dynamic types of parameters besides 
`self`:
        func tenMinutesAfter(date: NSDate) -> date.DynamicSelf {
                return date.adding(10 * 60)             // Note that `adding(_: 
NSTimeInterval)` returns DynamicSelf
        }
        
        // Possible alternative to generic syntax:
        func removingCommonPrefix(_ one: Collection, _ two: Collection) -> 
(one.Self.SubSequence, two.Self.SubSequence) where one.Self.Element == 
two.Self.Element, one.Self.Element: Equatable {
                for (oneIndex, twoIndex) in zip(one.indices + [one.endIndex], 
two.indices + [two.endIndex]) {
                        if oneIndex == one.endIndex || twoIndex == two.endIndex 
|| one[oneIndex] != two[twoIndex] {
                                return (one.suffixFrom(oneIndex), 
two.suffixFrom(twoIndex))
                        }
                }
                fatalError("Can't get here")
        }

The only disadvantage I see to this approach is that code which currently uses 
`Self` will be longer. But there may be other problems as well. I'm not 
entirely sure I have a good handle on the existing `Self` vs. `dynamicType`; 
it's possible the connection I see between them is spurious or ill-defined.

By the way, an alternative would be to leave the dynamic type as `Self` and 
call the static type `Type`, which I *think* would generalize the existing 
notion of the metatype being accessible as `Type`. In other words:

        Type.classMember()                              // Instead of 
ReallyLongClassName.classMember()
        foo.Type.classMember()                  // Likewise, but for a 
different variable
        
        let self2: Type                                 // Match the static 
type of self
        let foo2: foo.Type                              // Match the static 
type of a different variable
        
        Self.classMember()                              // Instead of 
self.dynamicType.classMember()
        foo.Self.classMember()                  // Likewise

        let self3: Self                                 // Match the dynamic 
type of self
        let foo3: foo.Self                              // Match the dynamic 
type of a different variable
        // (Those would probably require certain restrictions, like the base 
variable has to be 
        // immutable and the assignment has to come from a function returning a 
Self
        // derived from `self`/`foo`.)
        
        // Make promises about matching dynamic types of parameters besides 
`self`:
        func tenMinutesAfter(date: NSDate) -> date.Self {
                return date.adding(10 * 60)             // Note that `adding(_: 
NSTimeInterval)` returns Self
        }
        
        // Possible alternative to generic syntax:
        func removingCommonPrefix(_ one: Collection, _ two: Collection) -> 
(one.Type.SubSequence, two.Type.SubSequence) where one.Type.Element == 
two.Type.Element, one.Type.Element: Equatable {
                for (oneIndex, twoIndex) in zip(one.indices + [one.endIndex], 
two.indices + [two.endIndex]) {
                        if oneIndex == one.endIndex || twoIndex == two.endIndex 
|| one[oneIndex] != two[twoIndex] {
                                return (one.suffixFrom(oneIndex), 
two.suffixFrom(twoIndex))
                        }
                }
                fatalError("Can't get here")
        }

But I'm even *less* certain that `someVariable.Type` and `SomeClass.Type` are 
similar in any real sense, so I have my doubts about the wisdom of that one.

-- 
Brent Royal-Gordon
Architechies

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

Reply via email to