[swift-users] Which is the more idiomatic approach for pinning down the type of a generic parameter?
Marco Masser
lists at duckcode.com
Wed Jan 20 04:09:02 CST 2016
I want to write an extension on SequenceType that contains a set of methods named findFirst() that should return the first element that matches the given predicate.
For heterogeneous sequences where the caller is only interested in a specific subtype of elements, it is useful to have a variant of findFirst() that allows somehow specifying the type the caller is interested in so the predicate closure only gets called with that specific type.
That was probably not very easy to understand, but this example hopefully explains it clearly:
In an NSView’s subviews array (which are NSViews), find the first NSButton whose state is NSOnState. It would be neat if the predicate closure that checks the state property only gets passed NSButtons and no NSViews because that eliminates type checks in the closure.
I have two candidates for the implementation of this method and I’m looking for the more idiomatic one.
The examples work with a variable called containerView, which is of type NSView.
First try:
extension SequenceType {
@warn_unused_result
func findFirst<T>(_: T.Type, @noescape matching predicate: T throws -> Bool) rethrows -> T? {
for case let element as T in self where try predicate(element) {
return element
}
return nil
}
}
let result1 = containerView.subviews.findFirst(NSButton.self, matching: { $0.state == NSOnState })
Here, the first parameter (which is ignored in the implementation) pins down the type of the generic parameter. Note that the predicate’s parameter is of type NSButton so there’s no as? or something like that necessary.
But there’s another way to pin down the generic parameter:
extension SequenceType {
@warn_unused_result
func findFirst<T>(@noescape matching predicate: T throws -> Bool) rethrows -> T? {
for case let element as T in self where try predicate(element) {
return element
}
return nil
}
}
let result2: NSButton? = containerView.subviews.findFirst { $0.state == NSOnState }
This time, the generic parameter is inferred at call site by explicitly specifying the type of the result2 variable.
Note that with this implementation of findFirst() it’s also possible to pin down the generic parameter by giving the closure parameter a specific type:
let result3 = containerView.subviews.findFirst { (button: NSButton) in button.state == NSOnState }
The first implementation seems nice to me at the call site because users of the API can’t miss specifying the type of T. But I don’t really like the way NSButton.self looks and from a language standpoint, the first parameter is redundant. It’s even ignored in the implementation!
The second implementation seems more correct to me, but at the call site, it may be easy to miss specifying the resulting variable’s type, which leads to a compiler error that is not 100% obvious to newcomers (“Type of expression is ambiguous without more context” pointing at the $0), which leads to users thinking about what they did wrong, which leads to them coming to me asking how to use this method.
I’d love to hear comments on this and what you think is the better way to implement this. If that were a method in the Swift Standard Library, how would you expect it to work?
(Bonus question: Why isn’t there a method like this in the Standard Library?)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20160120/d9140306/attachment.html>
More information about the swift-users
mailing list