In order to make my code easier to read, I am using type aliases in Swift for various types of closures. I have the following basic set of closures:
public typealias FailureClosure = (error: NSError?) -> Void
public typealias ProgressClosure = (progress: Float32) -> Void
public typealias BasicClosure = () -> Void
I would like to add a closure typealias
that supports generic arrays, but I can't seem to figure out the syntax for it. This is as far as I am able to get, but I get the compile time error "Use of undeclared type 'T'"
public typealias ArrayClosure = <T>(array:[T]?) -> Void
Does anybody know how to do this? Or even if it is possible?
No, this is not currently possible. If it were possible, the syntax you'd expect would be:
public typealias ArrayClosure<T> = (array:[T]?) -> Void
You would then use it like ArrayClosure<Int>
. But it's not currently legal.
That said, I don't really recommend these type aliases. They obscure more than they illuminate. Compare this signature:
func foo(onError: FailureClosure)
with:
func foo(onError: NSError? -> Void)
To save just a couple of characters, you force the caller to guess what FailureClosure
passes. The error
or progress
tags don't really help you (you still need to use progress in ...
).
The one case that makes a lot of sense is around progress
, but I think the type you want there is:
public typealias Progress = Float32
Don't get me wrong here, type aliasing can be very helpful when it creates a new type. Progress
is a type that happens to be implemented as as float. But much of what you're doing (definitely with ArrayClosure
, and to a lesser extent with the others) is just creating new syntax without creating a new type, and that is more often confusing than helpful.
To call out your specific example, and why overuse of type aliases can cause you to overcomplicate your design:
func foo(failure: ((error: NSError?) -> ())? = nil)
You're right that this is really complicated. Compare:
func foo(failure: NSError -> Void = {_ in return})
Two big changes here. There's no reason to have a failure block that takes an optional error. Always pass an error (if there's no error, why would failure
be called?). And there's no reason for the failure block to be optional. If you really want a default value, just make the default value do nothing. Two optionals gone, and all the consuming and implementing code gets simpler. Always think carefully about whether something absolutely must be optional. Optionals add a lot of complexity; don't add them lightly.
Personally, I'd probably do this with an overload instead in many cases:
func foo(#failure: NSError -> Void) { ... }
func foo() {
foo(failure:{ _ in return })
}
I just think it's a little easier to understand what's going on. But either way is fine.
EDIT (Dec 2014): After writing Swift for a few more months, I've grown more fond of @David's approach in the comments below, which is to use an optional for the closure, but not for the error. Particularly given Swift's optional chaining syntax (failure?()
), it often does wind up being clearer.
func foo(failure: (NSError -> Void)? = nil)
Swift 4.1 supports generic type aliases. You can use this feature to provide a name for function with generic parameters.
You may have to use such declaration:
public typealias ArrayClosure<T> = ([T]?) -> Void
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With