There is some very strange business going on. It could perhaps be a bug. Consider the following modifications:
First Test
Change
typealias StyleClosure<T: UIView> = (T) -> ()
to
typealias StyleClosure<T: UIView> = (T) -> T
The compiler will then not allow the function +
to be called because the two closures passed in as parameters in the declaration of s3
are not the same, as is expected. There is certainty here.
Second Test
Attempt the following: print the types of the closures passed in as parameters from the closure returned by +
as follows:
func +<T>(lhs: @escaping StyleClosure<T>,
rhs: @escaping StyleClosure<T>) -> StyleClosure<T> {
return { (value: T) -> () in
lhs(value)
rhs(value)
print(type(of: rhs))
print(type(of: lhs))
}
}
Notice that s1
and s2
have type (UILabel) -> ()
, which is somwhat strange since s1
has type (UIView) -> ()
. It seems that the compiler allows for the coersion of s1
to (UILabel) -> ()
in order to satisfy the constraints of the generic function. In fact,
let s_weird = s1 as! StyleClosure<UILabel>
generates a warning that the cast always succeeds (which is indeed true) and that coercion should be used instead.
Now here comes the possible bug:
let thinksIsTrue = s1 is StyleClosure<UILabel>
print(thinksIsTrue)
The compiler issues a warning that the is
test is always true. However, the test actually evaluates to false
.
I have not run into a situation quite like this so there may be something deeper that I am missing with implicit coercion. There is some reason why the compiler might allow this. Since the UIView
closure could not modify a UILabel
in any strange way since UILabel
is a UIView
, coercion behind the scenes to a more specific closure type would allow for the possibility of calling both closures while satisfying the generic constraint, allowing us to call the closure with a narrower range of types. Since the intention is not very clear, this is perhaps undesirable.
If anybody has any other explanations let us know!