I implemented the following function -as an extension of array of booleans- which could throw a CustomError error:
enum CustomError: Error {
case empty
case doesNotContainTrue
}
extension Array where Element == Bool {
func indexOfFirstTrue() throws -> Int {
if isEmpty { throw CustomError.empty }
guard let detectedIndex = index(of: true) else {
throw CustomError.doesNotContainTrue
}
return detectedIndex
}
}
which works as expected:
let myArray = [false, true, false, true]
try print(myArray.indexOfFirstTrue()) // 1
Next, I tried to declare a function as:
func handleResult(_ index: Int) throws {
print(index * 2)
// ...
}
which should take the result of myArray.indexOfFirstTrue() and do something with it (for simplicity, let's assume that it prints the value multiplied by 2):
try handleResult(myArray.indexOfFirstTrue()) // 2
What I want to do is to declare handleResult as rethrowing function:
A function or method can be declared with the rethrows keyword to
indicate that it throws an error only if one of its function
parameters throws an error. These functions and methods are known as
rethrowing functions and rethrowing methods. Rethrowing functions and
methods must have at least one throwing function parameter.
The Swift Programming Language (Swift 4.1): Declarations - Rethrowing Functions and Methods.
So I can call it with non-throwing formula, thus it will not throws an error:
handleResult(myArray.indexOfFirstTrue()) // 2
But I am stuck of what should I edit to let it be a rethrowing function, so I tried to declare it as:
func handleResult(_ index: Int) rethrows {
print(index * 2)
}
and I got the error of:
error: 'rethrows' function must take a throwing function argument
therefore, I also tried to declare it as:
func handleResult(_ index: (() throws -> Int)) rethrows {
print(index * 2)
}
and obviously got the error of:
error: cannot convert value of type 'Int' to expected argument type
'() throws -> Int'
What should I do at this point?
Remember, the argument is of type () -> Int! So you need to call the function passed in to get the result! You also need try since the function can throw.
func handleResult(_ index: (() throws -> Int)) rethrows {
print(try index() * 2) // see the "()"?
}
Now you can use it like this:
let myArray = [true]
try handleResult(myArray.indexOfFirstTrue)
Related
I'm trying to write a function that can take any function as a parameter and execute it in Swift. I have tried this approach:
public func anyFunc<P, T> (_ function: (P...) -> T) {
_ = function()
}
and then trying it with:
anyFunc(print("hello"))
This produces ERROR: 'print' produces '()', not the expected contextual result type '(_...) -> _'
How can I achieve this (and is it feasible)?
How about just using #autoclosure, like so:
func anyFunc<T>(_ closure: #autoclosure () -> T) {
let result: T = closure()
// TODO: Do something with result?
}
anyFunc(print("hello"))
Initializer of class A takes an optional closure as argument:
class A {
var closure: ()?
init(closure: closure()?) {
self.closure = closure
self.closure()
}
}
I want to pass a function with an argument as the closure:
class B {
let a = A(closure: action(1)) // This throws the error: Cannot convert value of type '()' to expected argument type '(() -> Void)?'
func action(_ i: Int) {
//...
}
}
Class A should execute the closure action with argument i.
I am not sure about how to write this correctly, see error in code comment above. What has to be changed?
Please make your "what-you-have-now" code error free.
Assuming your class A like this:
class A {
typealias ClosureType = ()->Void
var closure: ClosureType?
init(closure: ClosureType?) {
self.closure = closure
//`closure` would be used later.
}
//To use the closure in class A
func someMethod() {
//call the closure
self.closure?()
}
}
With A given above, you need to rewrite your class B as:
class B {
private(set) var a: A!
init() {
//initialize all instance properties till here
a = A(closure: {[weak self] in self?.action(1)})
}
func action(i: Int) {
//...
}
}
The problem is that closure()? is not a type. And ()? is a type, but it is probably not the type you want.
If you want var closure to have as its value a certain kind of function, you need to use the type of that function in the declaration, e.g.
var closure: (Int) -> Void
Similarly, if you want init(closure:) to take as its parameter a certain kind of function, you need to use the type of that function in the declaration, e.g.
init(closure: (Int) -> Void) {
Types as Parameters
In Swift, every object has a type. For example, Int, String, etc. are likely all types you are extremely familiar with.
So when you declare a function, the explicit type (or sometimes protocols) of any parameters should be specified.
func swallowInt(number: Int) {}
Compound Types
Swift also has a concept of compound types. One example of this is Tuples. A Tuple is just a collection of other types.
let httpStatusCode: (Int, String) = (404, "Not Found")
A function could easily take a tuple as its argument:
func swallowStatusCode(statusCode: (Int, String)) {}
Another compound type is the function type. A function type consists of a tuple of parameters and a return type. So the swallowInt function from above would have the following function type: (Int) -> Void. Similarly, a function taking in an Int and a String and returning a Bool would have the following type: (Int, String) -> Bool.
Function Types As Parameters
So we can use these concepts to re-write function A:
class A {
var closure: (() -> Void)?
init(closure: (() -> Void)?) {
self.closure = closure
self.closure()
}
}
Passing an argument would then just be:
func foo(closure: (Int) -> Void) {
// Execute the closure
closure(1)
}
One way to do what I think you're attempting is with the following code:
class ViewController: UIViewController {
override func viewDidLoad() {
let _ = A.init(){Void in self.action(2)}
}
func action(i: Int) {
print(i)
}
}
class A: NSObject {
var closure : ()?
init(closure: (()->Void)? = nil) {
// Notice how this is executed before the closure
print("1")
// Make sure closure isn't nil
self.closure = closure?()
}
}
In Swift headers, the isSeparator: argument accepts a closure
public func split(maxSplit: Int = default, allowEmptySlices: Bool = default, #noescape isSeparator: (Self.Generator.Element) throws -> Bool) rethrows -> [Self.SubSequence]
But in the documentation, it lists closure syntax differently
{ (parameters) -> return type in
statements
}
How are you supposed to know that (Self.Generator.Element) throws -> Bool rethrows refers to a closure / requires a closure? Are there other ways that the headers/docs might list argument as meaning a closure?
The "thing" giving away that this is a closure is the ->. The full type is
(Self.Generator.Element) throws -> Bool
It means that the closure takes a variable of type Self.Generator.Element and has to return a Bool upon some calculation based on the input. It may additionally throw some error while doing so - that is what the throws is for.
What you then write
{ (parameters) -> return type in
statements
}
would be an actual implementation, a value of some generic closure type.
The type of a closure is for example (someInt:Int, someDouble:Double) -> String:
var a : ((someInt:Int, someDouble:Double) -> String)
Once again the thing giving away that a is actually a closure is the -> in the type declaration.
Then you assign something to a via some code snippet following your second code block:
a = { (integer, floating) -> String in
return "\(integer) \(floating)"
}
You can tell by the argument's type. Everything in Swift has a type, including functions and closures.
For example, this function...
func add(a: Int, to b: Int) -> Int { return a + b }
...has type (Int, Int) -> Int. (It takes two Ints as parameters, and returns an Int.)
And this closure...
let identity: Int -> Int = { $0 }
...has type Int -> Int.
Every function and closure has a type, and in the type signature there is always a -> that separates the parameters from the return value. So anytime you see a parameter (like isSeparator) that has a -> in it, you know that the parameter expects a closure.
the isSeparator definition means (Self.Generator.Element) throws -> Bool that you will be given an Element and you should return a Bool. When you will call split, you then can do the following :
[1,2,3].split(…, isSeparator : { element -> Bool in
return false
})
This is a pure silly example but that illustrates the second part of your question
I have the following toy example
func identity<T>(a : T) -> T{
return a
}
func applyIdentity<T>(f : T->T, t:T) -> T{
return f(t)
}
applyIdentity(identity, t:1)
And this works without a hitch. However, once I try to throw an exception in identity like so:
enum MyError: ErrorType{
case MyErrorFoo
};
func identity<T>(a : T) throws -> T{
if (true) {
return a
} else {
throw MyError.MyErrorFoo
}
}
...
The type checker complains on the applyIdentity(identity, t:1) call with message:
Argument for generic parameter 'T' could not be inferred
Any idea why this may be happening?
Your (second) identity() method can throw an error, therefore it has the type
T throws -> T, not T -> T.
If applyIdentity() should just forward
an error thrown in f() to the caller then you can define it as
func applyIdentity<T>(f : T throws ->T , t:T) rethrows -> T {
return try f(t)
}
See also "Declarations" in the Swift book:
Rethrowing Functions and Methods
A function or method can be declared with the rethrows keyword to
indicate that it throws an error only if one of it’s function
parameters throws an error. These functions and methods are known as
rethrowing functions and rethrowing methods. Rethrowing functions and
methods must have at least one throwing function parameter.
The enumerateLines function of Swift's String type is declared like this:
enumerateLines(body: (line: String, inout stop: Bool) -> ())
As I understand it, this declaration means: "enumerateLines is a function taking a closure, body, which is handed two variables, line and stop, and returns void."
According to the Swift Programming Language book, I believe I should thus be able to call enumerateLines in a nice terse fashion with a trailing closure, like this:
var someString = "Hello"
someString.enumerateLines()
{
// Do something with the line here
}
..but that results in a compiler error:
Tuple types '(line: String, inout stop: Bool)' and '()' have a different number of elements (2 vs. 0)
So then I try explicitly putting in the arguments, and doing away with the trailing closure:
addressString.enumerateLines((line: String, stop: Bool)
{
// Do something with the line here
})
...but that results in the error:
'(() -> () -> $T2) -> $T3' is not identical to '(line: String.Type, stop: Bool.Type)'
In short, no syntax that I've tried has resulted in anything that will successfully compile.
Could anybody point out the error in my understanding and provide a syntax that will work please? I'm using Xcode 6 Beta 4.
The closure expression syntax has the general form
{ (parameters) -> return type in
statements
}
In this case:
addressString.enumerateLines ({
(line: String, inout stop: Bool) -> () in
println(line)
})
Or, using the trailing closure syntax:
addressString.enumerateLines {
(line: String, inout stop: Bool) in
println(line)
}
Due to automatic type inference, this can be shortened to
addressString.enumerateLines {
line, stop in
println(line)
}
Update for Swift 3:
addressString.enumerateLines { (line, stop) in
print(line)
// Optionally:
if someCondition { stop = true }
}
Or, if you don't need the "stop" parameter:
addressString.enumerateLines { (line, _) in
print(line)
}