Generalized opaque and existential type constraints

One seemingly obvious approach that should be discussed, even if it can be shot down quickly, is to put “output types” in the regular angle brackets, with a sigil:

func groupedValues<C, out Output>(in collection: C) -> (even: Output, odd: Output) where ...

In addition to avoiding the “yet another parameter list in the middle” problem, it works nicely for continuations (should we add the requisite semantics):

func unzip<T, U, C: Collection<(T, U)>, out R1: Collection<T>, out R2: Collection<U>>
(_ collection: C) -> (R1, R2)

func unzipWithPyramidOfDoom<T, U, C: Collection<(T, U)>, out R1: Collection<T>, out R2: Collection<U>>
(_ collection: C, continuation: (R1, R2) -> Void)

Edit: I actually have something of a principled argument for this, not just aesthetics.

As I understand it, the intuition behind using some for inward type parameters as well as outward ones is that they’re essentially the same thing, just with information flowing in different directions. If we believe that, then using the same generalized syntax (but annotating the direction of information flow) seems like the most coherent option.

1 Like