Using Type Markers for 'is' Type Predicates to Cover Generic Types
Maybe you have some old code (you wouldn’t write this nowadays, of course) that uses someValue is SomeType
checks.
Maybe you need to check if someValue
is a generic type, like the following Box
:
struct Box<Content> { }
Then your is
predicates won’t work:
switch someValue {
case is Int,
is String,
is Box: // ❌ reference to generic type 'Box' requires arguments in <...>
doSomething()
default: break
}
The good news is that since you treat the concrete type as a mere marker, you can express this condition with a type marker protocol:
fileprivate protocol RequiresDoingSomething { }
extension Box: RequiresDoingSomething { }
extension Int: RequiresDoingSomething { }
extension Array: RequiresDoingSomething { }
// ...
Now you can use the is
test on this type marker:
guard someValue is RequiresDoingSomething else { return }
doSomething()
Why fileprivate
? The condition was a local affair. Only the old switch
statement knew which types to check. Making the protocol fileprivate and marking the types file-locally limits the type pollution in a similar way. (See "Locality of Information" and the _Single Responsibility Principle_.)
What’s this madness good for? I found the original is
check in old code that performed some quick fixes for values of some types passed through. So most things would pass through normally, but some needed to call a function (to ultimately update the UI as a fix for a display glitch). The values passing through are actually concrete ReSwift.Action
-conforming types, and only some of them required this change. Since an is
check didn’t work, this is the next best thing without having to rewrite the whole quick-fix-ability. Don’t do this in your own code, of course, it’s a bad sign 🙂