Opaque result types

In discussions about generalized existentials, my hope has been that rather than have explicit "opening" operations to use dynamic types, we could use "path dependent types" to refer to associated types relative to an existential:

let a: Collection
let b: a.Index = a.startIndex
let c: a.Element = a[b]

which is nice because it'd make operations relative to a generalized existential "just work" as expected without the mental overhead of having to think about a new opened type. Applying this idea to declarations, it might be interesting to allow constraints to be applied directly to arguments in a similar way. If return values could also be named, you could write things like this:

extension Collection {
  func concat(_ other: Collection) -> returned: Collection
    where Element == other.Element, Element == returned.Element

  func map(_ transform: (Element) -> returned.Element) -> returned: Collection
}

which feels nice and expressive to me, avoids the "angle bracket blindness" of explicit generic arguments, and could be made to do what you mean in both contravariant positions (where you want type parameters) and covariant positions (where you want opaque types). This would all be nicer if we hadn't already used protocols in type positions for existential types, although at least for non-inout arguments, taking an existential and taking an argument whose type is a unique generic type argument are equivalent.

7 Likes