Syntactic Sugar for nested types in extensions

Preamble: I had this idea while writing a lot of nested types, at first it seems like a great idea that reduced "Pyramid of doom" type issues.

I've since cooled on the idea, but I'm still on the fence and wanted to get some community feedback.

Currently nested types can be defined within extensions like so;

struct TopLevel {}

extension TopLevel {
    struct SecondLevel {}
}

extension TopLevel.SecondLevel {
    struct ThirdLevel {}
}

To reduce the nested indentation we could introduce shorthand like this;

extension struct TopLevel.SecondLevel.ThirdLevel.FinalLevel {
    // a struct defined as if we inside the ThirdLevel extension 
}

Obviously this would also extend to class and enum, e.g;

extension enum TopLevel.SecondLevel.ThirdLevel.FinalLevel.Colours: String {
    case red
    case green
    case blue
}

Would it make code less readable? Is the deeply nested type pattern common enough to make such sugar useful in the average code base?

Rich

2 Likes

I would like to do something like this, but I wouldn't require extension. If I do struct TopLevel.SecondLevel { ... } it could already check if there's a TopLevel somewhere and extend it automatically. I think it's fine to omit extension in this case as we're not really changing the type, we're only using its namespace.

10 Likes

Just to be clear, the alternative to

extension struct TopLevel.SecondLevel.ThirdLevel.FinalLevel {
    // a struct defined as if we inside the ThirdLevel extension 
}

is the currently-working

extension TopLevel.SecondLevel.ThirdLevel {
    struct FinalLevel {}
}

Is that right? I'm not sure I see the syntactic benefit to the alternate form. The current syntax is just as clear and concise.

Yes, the current system makes you write out some boilerplate-looking structure when you're nested four layers deep in structs that have no members and don't do anything. But in real code, each of these layers would have actual content and the structuring would not be unwelcome. Besides, there's nothing to stop you from writing it all out as one extension anyway...

2 Likes

I've wanted this syntax for a long time (without the extension prefix as @DeFrenZ mentioned). For me it's mostly a matter of indentation consistency. It's somewhat unfortunate that the body of struct A gets indented one level while struct A.B gets indented two levels. Not a critical issue, but it would be nice to allow top-level and nested types to be symmetrical in this sense, and I don't see any real downside to it.

2 Likes

Personally I use:

struct TopLevel {
    fileprivate func somethingIWantAccessTo() { ... }
}

struct FirstLevel {
    private let topLevel: TopLevel
    func somethingThatUsesTopLevel() {
        topLevel.somethingIwantAccessTo()
        ....
    }
}

Therefore can't see value in this change - sorry.

Apparently TopLevel and FirstLevel in your example aren't semantically and structurally related whatsoever, which means there isn't even a need for nesting them – the main problem discussed in this thread. That said, what's the point of your example?

Oops top level is a class - makes sense then - acts like Java inner class but avoids deep nesting with can be hard to follow at the expense of holding a pointer.

I believe you misunderstood. This has nothing to do with the construct (class, struct, etc) you use. Doing something like this...

..instead of this...

struct TopLevel {
    fileprivate func somethingIWantAccessTo() { ... }

    struct FirstLevel {
        private let topLevel: TopLevel

        func somethingThatUsesTopLevel() {
            topLevel.somethingIWantAccessTo()
            ...
        }
    }
}

...isn't even a workaround. It is as wrong as it would be to make Collection.Indices into simply Indices. Right now, the only correct method to semantically and structurally express the belonging of one type to another is nesting them or extending one. The whole point of such a relationship is for the nested type to be referred to as A.B outside the type it belongs to and simply as B within. Properties and methods act under the same rules. This thread proposes a usage convenience for this concept.

However, considering this bug is still present, the chances of the proposal being accepted and implemented are very low.

This is as about a good argument for as I can see.

And there are few (in any) drawbacks, that I can see.

1 Like

I don’t know how I’ve not encountered that bug!

Seems like we’re getting to a point int Swift’s maturity where bugs like that will start to be addressed.

It might however be a blocker for any implementation of this feature.

It seems like this concept has its supporters, and it’s detractors fall mainly in the camp of, “I wouldn’t use it” or “is it really useful?”.

I’ve not heard any philosophical arguments against it. And more importantly no technical reasons why it wouldn’t work.

If there are no major objections or technical areas we haven’t considered, I will start to investigate the feasibility of implementing it for a formal proposal.

I think extension shouldn't be written in the beginning.

3 Likes

Yes, I agree.

It’s better categorised as some this like, “Inferring extensions when when creating nested types”