Generic enums with Void associated values

I often find myself writing stuff like this:

enum Result<Value, Error> {
  case success(Value)
  case failure(Error)

extension Result: Equatable where Value: Equatable, Error: Equatable {}

But when working with e.g Result<Void, String> is is not equatable because Void isn't.

However, this isn't allowed:

extension Void: Equatable {
  static func == (lhs: Void, rhs: Void) -> Bool {
    return true

Is there an easy way to make such generic types over Void equatable without having to write a specific conformance to each type?

Also, I often find myself writing this extension:

extension Result where Value == Void {
  static var success: Result { return Result.success(()) }

Just to allow myself to write completion(.success) rather than completion(.success(()))

Is there a better way of doing this?

1 Like

I would argue that if there is no result value, hence void, you should rather use an Error? instead.

Result is just an example. This goes for any type that is generic, and when the you use Void as one of the type parameters.

However, in my case, I have lots of functions that work with Result. An entire HTTP client library. Lots of generic functions, and stuff to automatically deserialize json, etc.

It makes little sense to have some functions work with Result-based callbacks and other with Error?-based.

Result implements map, flatMap, convenience functions to turn a Result<U?, T> into Result<U, UnwrapError<T>> and so on.

I have built a Promise library on top of it, with Promise.promisify static functions for up to arity 4 to turn any function get(a: A, b: B, c: C, completion: (Result<U,T>) -> Void) into a get(a: A, b: B, c: C) -> Promise<U, T>.

It makes no sense to sometimes use a completely different type, such as Error?.

But sometimes, it does make sense to return Result<Void, _> and have it be Equatable. Maybe my generic response cache mechanism wants to do equality tests for some reason, etc.

How do I make it equatable? I can’t create two conditional conformances for a type even with different specializations.

The short answer is that it cannot be made to conform to Equatable, not even within the standard library itself.

Eventually, when it is possible to conform tuples to protocols, you will get this functionality automatically.

In the fullness of time, it would be wonderful to see fleshed-out subtyping relationships between T, T?, U?, and Result<T, U>.

But today, there is nothing you can do, short of implementing one or more major generics features in the Swift compiler.

1 Like

Related thread:

My workaround is to define something like enum Unit: Equatable { case unit }, producing Result<Unit> instead of Result<Void>.

Similarly have used: struct None: Equatable { }

1 Like