Key paths and case paths are just another name for (simple) lenses and prisms, and so they compose the same. In the lens hierarchy it's called an affine traversal, and Swift models this kind of getting/setting via optional-chaining.
We've called it an "optional path" before:
struct WritableOptionalPath<Root, Value> {
let get: (Root) -> Value?
let set: (inout Root, Value) -> Void
}
The big issue is that while key paths and case paths theoretically compose this way, we would need to introduce explicit conversions to make it work, which isn't the most ergonomic. We've found that these kinds of compositions fit more cleanly in Swift by structuring the composition in the operations themselves rather than composing the optic.
.ifLet(\.case) { // case key path
Scope(state: \.child) { // key path
…
}
}
// instead of:
.ifLet(
WritableOptionalPath(\.case).appending(
path: WritableOptionalPath(\.child)
)
) {
…
}
Of course, ideally Swift's native optics story evolves to the point of shipping first-class case key paths and writable optional key paths. Then you would be free to do the following:
.ifLet(\.case?.child) {
…
}
Added:
Many years ago Joe showed that traversals are possible in some capacity via key path subscripts: