It is not always obvious what the constraints are on a generic type or function, and I think it would be nice to have some way to programmatically discover them. I am envisioning this as a way to help programmers learn and understand things, as that is my primary use-case. In other words, I want to be able to print out a textual description of the generic constraints on something.
There are a few reasons this is valuable. First, just to be able to easily see how something is declared. And second, because sometimes the compiler applies additional constraints above and beyond what is written in source code, which can be unexpected and non-obvious. For example, see @DevAndArtist’s thread What kind of magic behavior is this?.
I recently encountered a similar situation while working on a super serious and important feature. Specifically, I wrote the following code:
infix operator --> : RangeFormationPrecedence
func --> <T: Comparable> (lhs: T, rhs: T) -> ReversedCollection<Range<T>> {
return (rhs ..< lhs).reversed()
}
Now, what types does the -->
operator work with? The only constraint listed is T: Comparable
, so it should work for anything Comparable
, right?
But wait, you can only form a ReversedCollection
from a Collection
, and Range
is only a Collection
when its Bound
is Strideable
with an integer Stride
. (Aside: how is a person expected to discover that if they don’t already know it? Yet another use-case for generic inspection!)
So why is my declaration even legal? After all, I haven’t provided enough constraints to ensure the return type is sensible. Not a problem, the compiler is happy to silently and, er, helpfully, insert the constraints on its own!
Observe:
let a = 5 --> 0 // works
let b = 5.0 --> 0.0 // fails
// error: type 'Double' does not conform to protocol 'SignedInteger'
So, in my playground where I’m testing this, I want to print out all the constraints on -->
that the compiler knows about. It would be nice to write something like Mirror(reflecting: -->)
, but of course that doesn’t work.
I want to be able to do this sort of inspection not only for generic functions, but also for generic types, and protocols with associated types. Essentially anywhere there might be a where
clause, I want to be able to see what the compiler knows about it.