Opaque Return type
Well, How does generic behave? 🤔
Generic function, class, structure, enum, or associatedtype are designed with such an abstract and general manner that their caller decides what type should be bound as the type of generic type’s placeholders and Generic code would work for all the types. For example:
Here at the time of calling printArray function whatever type is passed, Int and String for example in this code snippet are passed, They become deciding type for the caller. printArray method would work for all types because of its generic behaviour. For more details on generic please check Generic type in Swift.
Reverse Generic
Let’s just reverse the statement used for explaining the Generic above. Reverse generic means when return type or parameter type is decided inside its hosted code (like inside class, struct, enum, or function itself).
Opaque return type (ORT)
ORT implies when the caller doesn’t need to specify the underlying return type or parameter type and the caller must get concrete type for sure.
Let’s see different types of use cases, By which we can solve the problems with the help of Opaque return type:
UseCase 1: Self or associated type requirements 👇
Solution 1:
We can directly return the concrete type Mobile as per reverse Generic concept says that the caller must get concrete type.
But we have a problem here, If we set Mobile as return type, it will expose the return type to the caller it means doesn’t completely comply ORT. Let's see how to resolve this issue in Solution 2.
Solution 2:
Opaque return type comes to rescue from exposing the return type to the caller.
Here some UniqueProduct implies that manufactureProduct() will return a concrete type which must confirm UniqueProduct protocol. This exactly what reverse generic concept is.
The caller would not know the type of product whether it’s Mobile or Tablet. Here we are taking benefit of ORT which defined the return type as ORT by using some keyword.
UseCase 2: Hiding the complex return type by using the ORT 👇
In the above code snippet, same code is written in two functions getShortListedStudentList and getShortListedStudents.
The code written in function getShortListedStudentList is actually returning the type LazyMapSequence<LazyFilterSequence<LazySequence<[Student]>.Elements>, String>
so as the type of shortListedBySixtyCutOff constant would be the same due to type inference. With the help of ORT, we can hide these unnecessary type details.
The caller is much interested to get the result type only, which is simply Collection type here as per the requirement. so just by setting some Collection as return type of getShortListedStudents solves the issue of exposing all type details to shortListedBySeventyCutOff constant.
We can see the same approach is used for View protocol in SwiftUI
UseCase 3: You cannot cheat with ORT for its concrete type.
With ORT as return type, You cannot write such code that can potentially return more than one type if you do compiler won’t let you 😈.
Let’s see what it means:
Summary
- The use of Opaque return type is very specific as you can see in Usecase1 (Self or associated type requirements in Protocol) and Usecase2 (Hiding the complex return type by using the ORT).
struct VStack<Content> : View where Content : View
In SwiftUI, Observe such types like HStack, ZStack, Group, List, etc, they confirm View and also expect their generic type (Content) whatever it would be must confirm View.
- There are many such generic types in SwiftUI which can enclose other views that create a complex structure of type. ORT is very helpful to the client (caller) who will only know that this structure is some View.
- ORT makes your code more performant at runtime. Since protocols force compiler to check its underlying concrete type at runtime which imposes overhead on performance. but ORT gives surety of the concrete type to the compiler even at compile time.
- Generally in Application development, since we don't deal much with generics and Self or associatedtype in the protocol, I personally find very fewer reasons to use ORT, but on the other hand, If you are building the framework or library where you are going to use ORT use cases (1,2 and 3) extensively and you want to make your code flexible, highly reusable and with the abstraction of complex Generic types to the caller, you should use ORT.
- We need flexibility sometimes as a developer by returning more than one concrete type from a function based on certain value (different cases of enum let’s say) of parameter, at such places we cannot use ORT.
You can check more details about Opaque type in following links:
Swift-Book: Opaque Type
Proposal for Opaque return type: SE-0244
Thank you for reading the blog, let me know if more use-cases you find, we can add them here too. Share your thoughts on it.