This just popped into my head today. What if we could combine the ideas of:
withoutActuallyEscaping(_: do:)
AnyHashable
to work around slapping protocols onto tuples?
First, we make an AnyEquatable
:
public struct AnyEquatable {
public init<E: Equatable>(_ base: E) {
//...
}
public var base: Any { /*...*/ }
}
extension AnyEquatable: Equatable {
public static func == (lhs: AnyEquatable, rhs: AnyEquatable) -> Bool { /*...*/ }
}
extension AnyEquatable: CustomStringConvertible {
public var description: String { /*...*/ }
}
extension AnyEquatable: CustomDebugStringConvertible {
public var debugDescription: String {/*...*/ }
}
extension AnyEquatable: CustomReflectable {
public var customMirror: Mirror { /*...*/ }
}
Then, instead of slapping protocols onto tuples, we just pretend that we did:
func feigningEquatable<variadic T, R>(with tuples: [(T)], do body: ([AnyEquatable]) throws -> R) rethrows -> R
func feigningEquatable<variadic T, R>(with tuples: (T)..., do body: ([AnyEquatable]) throws -> R) rethrows -> R
We can carry this onto Hashable
:
func feigningHashable<variadic T, R>(with tuples: [(T)], do body: ([AnyHashable]) throws -> R) rethrows -> R
func feigningHashable<variadic T, R>(with tuples: (T)..., do body: ([AnyHashable]) throws -> R) rethrows -> R
Since it doesn't have Self
or associated type requirements, Encodable
is easier:
func feigningEncodable<variadic T, R>(with tuple: (T), do body: (Encodable) throws -> R) rethrows -> R
One more to go, but Decodable
wouldn't take a tuple, but give one out! We need something else:
func decodeCompound<variadic T>(as type: (T).Type, with decoder: Decoder) throws -> (T)
I use "compound" in the name in case we add more compound types:
func decodeCompound<variadic T>(as type: (;T;).Type, with decoder: Decoder) throws -> (;T;) // sum-tuple
func decodeCompound<T: [...; some Any]>(as type: T.Type, with decoder: Decoder) throws -> T // fixed-size array
Actually implementing this feature will have to wait until variadic generics are done. But I wanted to put it out here while it's fresh and put it in our collective consciousness for when we are ready.
A tuple can only work with EHC if all its component nominal types (which may be nested) do so. Feigned conformance is checked at compile time. The compiler should tell the user which component(s) aren't workable.
Another part is that using compound types as properties no longer disqualify a nominal type from automatic EHC conformance; the implementation just reuses the above functions.
Is this a good (preliminary) design, both in user experience or implement-ability?