After reading the links, I think path dependent typing can be summarised as "implicit unboxing of existentials". Looks nice and I see myself using it, though I would feel safer with explicit unboxing available as well, as a sort of fallback to troubleshoot complicated cases, or optimise compilation speed.
Thinking about how to apply implicit or explicit unboxing to @Dmitriy_Ignatyev's example, I see a couple of challenges:
// How result type should look like?
// We cannot use `le` in the result type, because it is out of scope.
// We need to erase the type, while preserving the fact that error type conforms to LocalizedError
func getResult<Success>() -> ??? {
let le: LocalizedError = getError() // Existential
// We need to specify `Success` while using unboxed error type of `le` as failure.
// What would the syntax look like?
let result: Result<???> = .failure(le) // Result<Success, type(of: le)>
return result
}
Applying Syntax for existential type as a box for generic type and Placeholder types to address those, solution may look like this:
func getResult<Success>() -> any<Failure: LocalizedError> Result<Success, Failure> {
let le: LocalizedError = getError() // Existential
let result: Result<Success, _> = .failure(le) // Result<Success, type(of: le)>
return result
}
This also avoids the need for self-conformance, instead "existentialness" bubbles up. And cases where we currently use Result<S, any Error>
become any<F: Error> Result<S, F>
. Since that's a pretty common case, this probably deserves a typealias in the standard library:
typealias AnyErrorResult<S> = any<F: Error> Result<S, F>