# Allow opaque result types in protocol requirements

**URL:** <https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926>\
**Category:** Discussion\
**Created:** [August 27, 2022, 2:19pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926 "2022-08-27T14:19:07Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [August 27, 2022, 2:19pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/1 "2022-08-27T14:19:07Z")

</div>

I'm trying to model a generic transformation. There should be a protocol requirement, and that requirement accepts a generic collection and returns some Collection of transformed contents.

For instance, the transformation may return a `LazyMapCollection`:

```swift
func transform<Source>(
  _: Source
) -> LazyMapCollection<Source, UInt8> where Source: Collection, Source.Element == UInt8 {
  // ...
}

```

My understanding is that this is a sort of "higher-kinded type" - `Source` is used both by input type and output type. Indeed, it is impossible to express this today in Swift.

```swift
protocol Transformer {
  associatedtype TransformedData: Collection where EncodedData.Element == UInt8
  func transform<Source>(_: Source) -> TransformedData where Source: Collection, Source.Element == UInt8
}

struct MyTransformer {

  // 'TransformedData' would be inferred as LazyMapCollection<Source, UInt8>
  // from the function signature, but that clearly doesn't make sense here
  // because we don't know what 'Source' is.
  typealias TransformedData = ???

  func transform<Source>(
    _: Source
  ) -> LazyMapCollection<Source, UInt8> where Source: Collection, Source.Element == UInt8 {
    // ...
  }
}

```

This is very limiting. This function cannot return any generic wrappers at all - it will have to copy data from all `Source` types to a single type (e.g. an `Array`), or erase the `LazyMapCollection` in an existential. In summary, it must ensure that `Source` no longer appears on both sides of the function signature:

```swift
struct MyTransformer {

  typealias TransformedData = AnyCollection<UInt8>
  // ^^^^^^^^^^^^^^^^^^^^
  // no longer mentions 'Source'

  func transform<Source>(
    _: Source
  ) -> AnyCollection<UInt8> where Source: Collection, Source.Element == UInt8 {
    // ^^^^^^^^^^^^^^^^^^^^ - no longer mentions 'Source'
  }
}

```

But there is, in theory, another option. Why couldn't we use an opaque type? This would totally work as a free function:

```swift
func transform(_: some Collection<UInt8>) -> some Collection<UInt8>

```

And it does what we want - it breaks the type dependency between the function's input and output, but in a way that allows the implementation to avoid copying or existential boxing. However, this appears to be banned in protocol requirements:

```swift
protocol Transformer {
  func transform(_: some Collection<UInt8>) -> some Collection<UInt8>
  // error: 'some' type cannot be the return type of a protocol requirement; 
  // did you mean to add an associated type?
}

```

This limitation is mentioned in [SE-0244](https://github.com/apple/swift-evolution/blob/main/proposals/0244-opaque-result-types.md#restrictions-on-opaque-result-types), but I don't find the reasoning to be satisfactory (emphasis added):

> More fundamentally, opaque result types cannot be used in the requirements of a protocol:
> 
> ```
> protocol Q { func f() -> some P // error: cannot use opaque result type within a protocol }
> 
> ```
> 
> **Associated types provide a better way to model the same problem** , and the requirements can then be satisfied by a function that produces an opaque result type. (There might be an interesting shorthand feature here, where using `some` in a protocol requirement implicitly introduces an associated type, but we leave that for future language design to explore.)

I would like to suggest that associated types _cannot always_ be used in place of opaque types, and that there are valuable use-cases for opaque result types in protocol requirements beyond simply being shorthands for associated types.

Again, the function I am trying to express works already as a free function, so I don't see any reason why it could not also be a protocol requirement. Is there some implementation reason why it cannot be supported?

If possible, I would like us to lift this restriction. There are interesting models which we cannot express today.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [August 27, 2022, 6:14pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/2 "2022-08-27T18:14:04Z")

</div>

The requirement does not have to match the implementation. You can write the requirement the old way and the implementation the new way and rely on associated type inference.

I don’t think it would break the language to support the other way for protocols, either. It would be effectively shorthand for the old way, except that the associated type wouldn't have a name you can write in source. But I don’t know if we need it, either; I’d like to see more motivation first. (We could definitely add a fix-it, though.)

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [August 27, 2022, 6:15pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/3 "2022-08-27T18:15:24Z")

</div>

…oops, strike ALL of that, I see the problem now. This would indeed be a new feature.

EDIT: to spell it out clearly, a normal associated type is a function of `Self` (e.g. “String.Subsequence = Substring”). A normal opaque result type for a protocol extension method is a function of the input generic types including Self (e.g. “String.lazy.map produces a `LazyMapCollection<String, ResultElement>` even if you can’t see it”. A requirement opaque result type would be more like the protocol extension opaque result type case than an associated type.

---

<div class="post-metadata">

**Author:** ![taylorswift](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/taylorswift/32/43413_2.png) [@taylorswift](https://forums.swift.org/u/taylorswift)\
**Post date:** [August 27, 2022, 6:44pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/4 "2022-08-27T18:44:21Z")

</div>

[`swift-grammar`](https://github.com/kelvin13/swift-grammar/blob/master/Sources/Grammar/Rule/ParsingRule.spf)’s [`ParsingRule`](https://swiftinit.org/reference/swift-grammar/grammar/parsingrule/) has a similar problem, where it has a requirement [`parse(_:)`](https://swiftinit.org/reference/swift-grammar/grammar/parsingrule.parse%28_:%29?overload=s7Grammar11ParsingRuleP5parsey12ConstructionQzAA0B5InputVyqd __GzKAA0B11DiagnosticsRd__ 6Source_5IndexQYd __8LocationRtzAK_7ElementQYd__ 8TerminalRtzlFZ) which can take many different kinds of input, but must return a fixed [`Construction`](https://swiftinit.org/reference/swift-grammar/grammar/parsingrule/construction) type. the solution i went with was to add more `associatedtype` axes to `ParsingRule`, to accomodate generic-ness in the parsing output.

the doccomment for the `associatedtype` [`Location`](https://swiftinit.org/reference/swift-grammar/grammar/parsingrule/location) explains the workaround in more detail.

in your example, this would mean making `MyTransformer` generic, and using the generic parameter as the type witness.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [August 27, 2022, 7:01pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/5 "2022-08-27T19:01:46Z")

</div>

> [@jrose](#):
>
> EDIT: to spell it out clearly, a normal associated type is a function of `Self` (e.g. “String.Subsequence = Substring”). A normal opaque result type for a protocol extension method is a function of the input generic types including Self (e.g. “String.lazy.map produces a `LazyMapCollection<String, ResultElement>` even if you can’t see it”. A requirement opaque result type would be more like the protocol extension opaque result type case than an associated type.

Right. So, if an opaque type is defined by its generic environment, then for a non-generic protocol requirement (say, SwiftUI's `View.body`), an opaque return type is the same as an associated type (the generic environment is just `Self`).

But in a _generic_ protocol requirement, there are additional elements that make up the generic environment (in this case, a `Source` collection). In a way, it is similar to a generic associated type.

If we had GATs, the transformer could be written as follows:

```swift
protocol Transformer {
  // Generic associated type
  associatedtype TransformedData<Source>: Collection
  where Source: Collection, Source.Element == UInt8,
        TransformedData.Element == UInt8

  func transform<Source>(
    _: Source
  ) -> TransformedData<Source>
}

```

This would more rigorously model the relationship between the function's input and return types, possibly allowing for additional constraints (e.g. we could say that `TransformedData` and `Source` must have the same `Index` type, if we cared about that).

In this case, I don't need to preserve that information, so we don't need to get in to the whole business of expanding the type system to handle those. An opaque result type would do just fine.

Also worth mentioning: opaque result types can also be defined by [availability conditions](https://github.com/apple/swift-evolution/blob/main/proposals/0360-opaque-result-types-with-availability.md) now (not just their generic environments), so there is an additional way in which they are not the same as associated types.

---

<div class="post-metadata">

**Author:** ![filip-sakel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/filip-sakel/32/8697_2.png) [@filip-sakel](https://forums.swift.org/u/filip-sakel)\
**Post date:** [August 27, 2022, 7:19pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/6 "2022-08-27T19:19:36Z")

</div>

SwiftUI’s ViewModifier is another example of where generic associated types would be useful. Currently, the makeBody requirement accepts an existential so that the return type can be a normal associated type. I think the intended API was for makeBody to be generic over its input in addition to being opaque.

---

<div class="post-metadata">

**Author:** ![Stan\_Smida](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/stan_smida/32/10999_2.png) [@Stan\_Smida](https://forums.swift.org/u/Stan_Smida)\
**Post date:** [October 16, 2023, 2:53pm UTC](https://forums.swift.org/t/allow-opaque-result-types-in-protocol-requirements/59926/7 "2023-10-16T14:53:10Z")

</div>

+1

I hit this just today and I'm surprised Swift doesn't allow that.

```swift
protocol Storage {
    func list<T: StorageableEntity>(_ objectsOf: T.Type) async throws -> some Sequence<T>
}

```
