# Dealing with Nested Types when Documenting Extensions with DocC

**URL:** <https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099>\
**Category:** Swift-DocC\
**Tags:** docc\
**Created:** [September 5, 2022, 10:41am UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099 "2022-09-05T10:41:50Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![theMomax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/themomax/32/22719_2.png) [@theMomax](https://forums.swift.org/u/theMomax)\
**Post date:** [September 5, 2022, 10:41am UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/1 "2022-09-05T10:41:51Z")

</div>

A couple of months back I started a pitch on how to [Document Extensions to External Types Using DocC](https://forums.swift.org/t/document-extensions-to-external-types-using-docc/56060). While there will be a few PRs with improvements later on, the initial version is pretty much ready for merge. However, before that, I need your help once more to decide how to deal with extensions to **nested** types.

This decision influences at what URL the pages for extensions to nested types such as SymbolKit's `SymbolGraph.Symbol` are located and therefore also influences how these pages can be referenced using symbol links.

The original proposal defined the following location for extended type pages:

> [@Document Extensions to External Types Using DocC](https://forums.swift.org/t/document-extensions-to-external-types-using-docc/56060/26):
>
> hostpath/EXTENDING\_MODULE\_NAME/EXTENDED\_MODULE\_NAME/EXTENDED\_TYPE\_NAME

However, it didn't further specify how the `EXTENDED_TYPE_NAME` segment would look like for extensions to nested types.

## Approaches

I want to propose two approaches which I have already implemented and hope that you can help me decide which one is favorable.

### Path Contraction

The first approach - I call it "path contraction" - cuts all container type names from the `EXTENDED_TYPE_NAME` segment, so that literally only the "name" of the extended type remains.

For example, the extended type page for SymbolKit's `SymbolGraph.Symbol` would be located at `hostpath/EXTENDING_MODULE_NAME/SymbolKit/Symbol`.

You can find the PR for this approach [here](https://github.com/apple/swift-docc/pull/335).

### Hierarchical

The second approach is a hierarchical one. It creates extended type pages for all container types of extended nested types, even if these container types are not extended themselves.

Thus, the extended type page for SymbolKit's `SymbolGraph.Symbol` would be located at `hostpath/EXTENDING_MODULE_NAME/SymbolKit/SymbolGraph/Symbol`. Furthermore, `hostpath/EXTENDING_MODULE_NAME/SymbolKit/SymbolGraph` would also be a valid page URL, independently of whether or not `SymbolGraph` itself is extended.

## Evaluation

IMO, both these approaches have advantages and disadvantages in certain situations. I created a [small project with documentation catalog](https://github.com/theMomax/swift-docc-sample-nested-types) that should serve as support for discussing these aspects. I compiled this same catalog using both approaches. You can find the documentation archive resulting from path contraction [here](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/) and the hierarchical version [here](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/).

These are the aspects I think are important to consider:

### Reference Intuitiveness

I think one major advantage of the hierarchical approach is that the URL and therefore also the referencing schema follows the same rules that we know from Swift.

For example:

```swift
public extension SymbolGraph.Symbol {
    func foo() {}
}

```

IMO, [```SymbolKit/SymbolGraph/Symbol``` (hierarchical)](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/symbolgraph/symbol) is more intuitive than just [```SymbolKit/Symbol``` (path contraction)](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/symbolkit/symbol-swift.struct.extension).

### Collisions

This becomes even more apparent if we consider how many more collisions path contraction can cause, which have to be resolved using hash- or type-based disambiguation suffixes. The sample project also features an extension to `UnifiedSymbolGraph.Symbol`:

```swift
public extension UnifiedSymbolGraph.Symbol {
    func foo() {}
}

```

Therefore, the actual references for path contraction are the following:

- [```SymbolKit/Symbol-swift.struct.extension```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/symbolkit/symbol-swift.struct.extension)
- [```SymbolKit/Symbol-swift.class.extension```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/symbolkit/symbol-swift.class.extension)

The hierarchical approach doesn't have this problem as it contains the unambiguous path segments `SymbolGraph` and `UnifiedSymbolGraph`, respectively:

- [```SymbolKit/SymbolGraph/Symbol```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/symbolgraph/symbol)
- [```SymbolKit/UnifiedSymbolGraph/Symbol```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/unifiedsymbolgraph/symbol)

Note, that with path contraction there can also be a collision between a nested type and its container types such as in this example:

```swift
struct Nested {
    struct Nested {
        struct Nested {}
    }
}

```

Here, all three types can only be disambiguated using a hash-based disambiguation suffix.

### Cluttering

However, the added path segments in the hierarchical approach can also have a negative impact:

```swift
public extension SymbolGraph.Symbol.Swift.GenericConstraint.Kind {
    func foo() {}
}

```

While we do also extend `SymbolGraph.Symbol`, we don't extend `SymbolGraph.Symbol.Swift` or its nested type `GenericConstraint`. Therefore, the hierarchical approach generates two extended type pages that are basically empty, i.e. they only list their extended child:

- [```SymbolKit/SymbolGraph/Symbol/Swift```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/symbolgraph/symbol/swift)
- [```SymbolKit/SymbolGraph/Symbol/Swift/GenericConstraint```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/symbolgraph/symbol/swift/genericconstraint)

With path contraction, these two pages do not exist, and [```SymbolKit```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/symbolkit) links to [```SymbolKit/Kind```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/symbolkit/kind) directly.

### Cross-Target Nesting

One additional complexity surfaces when considering cross-target nesting - which I think is a rather rare use-case.

In our sample project, the secondary target `SomeSymbolKitExtension` extends SymbolKit's `SymbolGraph` as follows:

```swift
public extension SymbolGraph {
    struct ExternalType { }
}

```

The main `NestedTypes` target (which imports `SomeSymbolKitExtension`) extends this nested `ExternalType`:

```swift
public extension SymbolGraph.ExternalType {
    func foo() {}
}

```

In the hierarchical approach, this extension would be part of the extended module `SymbolKit`, because if we define a hierarchy between the extended types, an extension to the inner type is always also an extension to the context it lives in. Therefore, the respective extended type page is located at [```SymbolKit/SymbolGraph/ExternalType```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit/symbolgraph/externaltype), i.e. as part of the extended module [```SymbolKit```](https://themomax.github.io/swift-docc-sample-nested-types-hierarchical/documentation/nestedtypes/symbolkit). This follows the Swift ruleset, where the most qualified identifier for the extension is `SymbolKit.SymbolGraph.ExternalType`.

With path contraction, on the other hand, we view `SymbolGraph.ExternalType` independently of its parent type `SymbolGraph`, and thus consider this an extension to the extended module `SomeSymbolKitExtension`. The respective pages are located at [```SomeSymbolKitExtension/ExternalType```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/somesymbolkitextension/externaltype) and [```SomeSymbolKitExtension```](https://themomax.github.io/swift-docc-sample-nested-types-path-contraction/documentation/nestedtypes/somesymbolkitextension).

### Implementation

Overall, the implementation for path contraction is a little more complicated as it requires some adaptions to path resolution and disambiguation logic in order to deal with contracted path elements. However, I think the difference is small enough to base this decision first and foremost on user experience.

* * *

I personally think the hierarchical approach is better as its only real disadvantage is cluttering. However, very deep nesting is rather uncommon and if really necessary, one can "hide" the empty pages using manual curation.

Please let me know what you think, which approach you prefer, and if I've missed any important usability aspects!

Thanks for your help!

---

<div class="post-metadata">

**Author:** ![daniel-grumberg](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/daniel-grumberg/32/22029_2.png) [@daniel-grumberg](https://forums.swift.org/u/daniel-grumberg)\
**Post date:** [September 5, 2022, 11:02am UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/2 "2022-09-05T11:02:45Z")

</div>

You make a lot of good points here! I personally agree that the hierarchical approach seems better here, if only because it follows the Swift ruleset. I think we could strive down the line for a mix of both approaches. I think that the links should follow the hierarchical approach, but if the intermediate pages are empty, we could automatically curate the leaf pages in the right spot in the extended types hierarchy.

---

<div class="post-metadata">

**Author:** ![finestructure](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/finestructure/32/34879_2.png) [@finestructure](https://forums.swift.org/u/finestructure)\
**Post date:** [September 5, 2022, 11:10am UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/3 "2022-09-05T11:10:08Z")

</div>

+1 on the hierarchical approach! Feels like it’s easier to explain and the “con” case seems rather convoluted and therefore should be rare.

---

<div class="post-metadata">

**Author:** ![theMomax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/themomax/32/22719_2.png) [@theMomax](https://forums.swift.org/u/theMomax)\
**Post date:** [September 5, 2022, 12:03pm UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/4 "2022-09-05T12:03:42Z")

</div>

> [@daniel-grumberg](#):
>
> I think we could strive down the line for a mix of both approaches. I think that the links should follow the hierarchical approach, but if the intermediate pages are empty, we could automatically curate the leaf pages in the right spot in the extended types hierarchy.

That's a great idea and definitely something I can look into later on. Since curation doesn't influence the URLs, we can also iterate on this without causing a breakage.

---

<div class="post-metadata">

**Author:** ![scanon](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/scanon/32/2445_2.png) [@scanon](https://forums.swift.org/u/scanon)\
**Post date:** [September 5, 2022, 12:19pm UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/5 "2022-09-05T12:19:10Z")

</div>

Agreed. The hierarchical approach seems obviously more correct.

---

<div class="post-metadata">

**Author:** ![theMomax](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/themomax/32/22719_2.png) [@theMomax](https://forums.swift.org/u/theMomax)\
**Post date:** [September 7, 2022, 5:45am UTC](https://forums.swift.org/t/dealing-with-nested-types-when-documenting-extensions-with-docc/60099/6 "2022-09-07T05:45:16Z")

</div>

This seems to be decided. I'll close [https://github.com/apple/swift-docc/pull/335](https://github.com/apple/swift-docc/pull/335) in favor of [https://github.com/apple/swift-docc/pull/369](https://github.com/apple/swift-docc/pull/369).

Thanks for your feedback!
