[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