[swift-evolution] [Pitch] Adding a Self type name shortcut for static member access
Brent Royal-Gordon
brent at architechies.com
Mon Apr 4 20:53:31 CDT 2016
> 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
More information about the swift-evolution
mailing list