[Idea] Reference to enclosing type by `Parent`, akin to `Self` but "one level up"

Sometimes we would greatly benefit from being able to access the "parent TYPE" inside some "nested type"/"child type". For example this TCA code:

@Reducer
struct Onboarding {
	var body: some ReducerOf<Self> { ... } // `Self` is `Onboarding` 
	struct View: SwiftUI.View { // `Self` is `Onboarding.View`, cannot access type `Onboarding` here...
		let store: StoreOf<Onboarding> // thus have to spell out `Onboarding` here
		init(store: StoreOf<Onboarding>) { // and here...
			self.store = store
		}
	}
}

I want to propose being able to access the 'parent type' by use of Parent (which is undefined in a non nested context (would be a compiler error)). Allowing:

@Reducer
struct Onboarding {
	var body: some ReducerOf<Self> { ... } // `Self` is `Onboarding` 
	struct View: SwiftUI.View { // `Self` is `Onboarding.View`, cannot access type `Onboarding` here...
		let store: StoreOf<Parent> // 🎉
		init(store: StoreOf<Parent>) { // 🎉
			self.store = store
		}
	}
}

This may look like a small thing... but this would allow us to write more powerful Xcode Templates and I believe it will be helpful for macro authors as well.

There is already kind of an asymmetry wrt "type access", a nested type can reference a "sibling type", see e.g. AsyncBufferSequence.Iterator being able to access StorageType and Element which are types of AsyncBufferSequence (aka "sibling types") in swift-async-algorithm. When a nested type access a "sibling type", making a translation to filesystem that is doing ../other, so clearly Swift is able to access the parent type (and then drill down to another neseted type inside it (aka "sibling type)), but we lack the syntax for accessing only ../ - I propose we call it Parent.

What say you?

8 Likes

I think the phrase is "+1 from me". It expresses the intent of referring to the parent itself as opposed to a specific type that just happens to be the parent, which makes the child type's behaviour clearer when refactoring and also more amenable to copy-and-pasting into other parent types.

1 Like

This is indeed something I've been wishing for since Swift 2.

1++, for this would bring the language one step closer to being complete.

I think we should NOT use Super since it makes one think about classes and inheritance, which is... a whole different dimension/"axis". That would be inheritance dimension, whereas this is "type nesting" dimension.

I think Super would be good also, for inheritance, but I will leave that for another thread...

4 Likes

Both Parent and Super has tight bounding to the inheritance, while it is about nested types.

As for the issue itself, I don't think that is a huge enough issue to make it part of the language. A simple typealias will do the work:

typealias Store = StoreOf<Onboarding>

I also think that such nesting mostly isn't good (and this mix of a view and reducer particularly) at a large scale, should be used rarely and isn't encouraged by the language tools.

5 Likes

For macros, this would be solved with the following feature instead,

Additionally, this adds a special rule for "types" and adds a new keyword - that's only confusing. How would this also work with types actually called Parent, or any other proposed keyword?

the typealias would have to have the same access level as any signatures that use it, which would generate API pollution for public APIs.

1 Like

To circumvent that a few project might have a nested type called Parent, one might introduce a new symbol for this kind of language provided type aliases (not typealias):
$Self (and let Self simply by a typealias Self = $Self for backwards compatibility)
$Parent (which is only defined on nested types, obviously).

(or §Parent or some other symbol...)

I agree here. May be OuterType or EnclosingType?

This is a good question. I think we should use a syntax that prevent any such collisions, like $wrappedProperty with property wrappers. Something like $EnclosingType / @EnclosingType

For now trying to declare a struct $Parent turns in compiler error: the '$' prefix is reserved for implicitly-synthesized declarations

Yes I was gonna suggest EnclosingType too, but felt it was a bit verbose. But clarity over brevity!

I think getting this through the .Type metatype would be clear and brief enough: Self.Type.Parent? I'm not really sure what would happen to top level types unless we give it a parent type of Void, if that is theoretically correct.

2 Likes

Void Parent type is an incorrect mental model as all top level types are 1) not nested types 2) are not in Void type namespace, they are in module namespace.
I would prefer compiler error for attempt to reference Enclosing type from top level type.