Nested functions error in swift - swift

I am trying to use nested functions in my Swift Program.
func test()->Bool{
func test1()->Bool{
return true
}
func test2()->Bool{
return true
}
func test3()->Bool{
return true
}
return test1 && test2 && test3
}
Its giving me some error! But if i try to
return test1
Its working fine.
Please explain why its giving me error on applying operators in nested function.

There are two approaches to nested functions/closures:
As you have it:
func test() -> Bool {
func test1() -> Bool {
return true
}
func test2() -> Bool {
return true
}
func test3() -> Bool {
return true
}
return test1() && test2() && test3()
}
Or saving it to closures and calling them:
func test() -> Bool {
let test1 = { () -> Bool in
return true
}
let test2 = { () -> Bool in
return true
}
let test3 = { () -> Bool in
return true
}
return test1() && test2() && test3()
}
The difference is really small and it comes down to your own preference but straight from documentation:
Nested functions are closures that have a name and can capture values from their enclosing function.
and
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context.
Either way, both cases have to be called with () at the end of the name to perform their action, otherwise you're returning a function and not its evaluation. Your return test1 would be only valid in this case:
func test() -> (() -> Bool) {
func test1() -> Bool {
return true
}
return test1
}
where your function's return type is a function that returns Bool after you evaluate it. You want to evaluate though and return the Bool value combined with logical AND statements. Thus, you have to evaluate each function to get each function's Bool value to combine it and return the final value.

return test1 should fail as well, unless you have a test1 property in your class. It seems you are simply forgetting to call these as functions.
return test1() && test2() && test3()
will work just fine.

Related

How to invoke any swift function conditionally (without if block)

I want to write a general-purpose Swift function that serves the following simple purpose:
Take any function as argument
Take a Bool argument
If the bool argument is TRUE, invoke the input function with its args. Otherwise No-op.
The purpose is to eliminate a lot of clumsy if statements in the code that meet a specific criteria.
Something like:
typealias ClosureType = (Any...) -> Any. // Notice the variable argument of any kind
func invokeIfConditionIsTrue(closure: Closure, condition: Bool) {
if condition {
if let myFunc = closure as? ClosureType {
myFunc()
print("executed")
} else {
print("not executed")
}
}
}
func testIntToInt(i: Int) -> Int {
return i*i
}
func testIntToDouble(i: Int) -> Double {
return Double(i*i)
}
invokeIfConditionIsTrue(testIntToInt, true). // executed
invokeIfConditionIsTrue(testIntToDouble, false). // not executed
However, I am struggling to come up with syntax that will enable the argument passing to the input myFunc() func.
The example is pretty basic, and my input function closure could be accepting and emitting any type of input/outputs, including structs, classes and objective c stuff.
I have a hunch this is possible via a mechanism called function object, but I am not familiar enough with it.
Should I reinvent the wheel, or is there already a library/known way which is doing it successfully, and I am missing out?
I have no idea why you think
invokeIfConditionIsTrue(testIntToInt, condition)
is somehow superior to
if condition { result = testIntToInt(n) }
Or
result = condition ? testIntToInt(n) : 0
but what you want is pretty much impossible unless you wrap the function in a closure because there is no way to express "function with any arguments" in Swift as a type. The best you can do is wrap your function in a closure with known argument types. There's also no general Closure type that represents any closure.
func invokeIfConditionIsTrue(closure: () -> (), condition: Bool) {
if condition {
closure()
print("executed")
}
}
invokeIfConditionIsTrue(closure: { result = testIntToInt(n) }, condition: true)
But, as you can see, that's not really any better than an if statement. In fact, it's much worse.
Another possibility is to define a function that returns a function, but it still needs to know the argument types.
func invokeIfConditionIsTrue(closure: (Int) -> Int, condition: Bool) -> (Int) -> Int?
{
if condition {
return closure
}
else
{
return { _ in 0 } // A dummy function
}
}
invokeConditionIfTrue(closure: testIntToInt, condition: true)(n)
After some haggling and searching for syntax (I wasn't good in FP, as I mentioned in the beginning), I was able to compile and run the below solution in my XCode 14.2 playground.
My desired function:
func executeIfCondition(function: (#escaping (Any...) -> Any), condition: Bool) {
if condition {
function()
}
}
This was supposed to replace following types of calls across my codebase:
if condition {
function()
}
Test functions whom I want to invoke, using executeIfCondition if condition = true.
func printStringAndReturnInt(i: Int, s: String) -> Int {
print(s)
return i
}
func printInt(i: Int) -> Void {
print("\(i)")
}
func printArray(arr: [Int]) -> Void {
arr.forEach({ print("\($0)") })
}
struct Struct1 {
var value1: Int
var value2: Int
}
func printStruct(t: Struct1) {
print("Struct1: \(t.value1) - \(t.value2)")
}
class Class1 {
var value1: Double = 100.0
var value2: Double = 200.0
}
func printClass(c: Class1) {
print("Class1: \(c.value1) - \(c.value2)")
}
Example Usage:
Instead of:
if (true) {
printStringAndReturnInt(i: 5, s: "Wow!")
}
I will now use:
executeIfCondition(function: { _ in printStringAndReturnInt(i: 5, s: "Wow!") }, condition: true)
The rest:
executeIfCondition(function: { _ in printInt(i: 61) }, condition: false)
executeIfCondition(function: { _ in printArray(arr:[9,10,11,12]) }, condition: true)
executeIfCondition(function: { _ in printStruct(t: Struct1(value1: 100, value2: 200)) }, condition: true)
executeIfCondition(function: { _ in printClass(c: Class1()) }, condition: true)
This isn't exhaustive still, but enough for a start.

Why is the function which seems to be returning print declared Void?

The code in question is below:
class Singer {
func playSong() {
print("Shake it off!")
}
}
func sing() -> () -> Void { // Void???
let taylor = Singer()
let singing = {
taylor.playSong()
return
}
return singing
}
let singFunction = sing()
singFunction()
Bonus question: I didn't quite understand declaration:
func sing() -> () -> Void
Couldn't we just declare it as?:
func sing() -> Void
You should read the declaration of sing as:
func sing() -> (() -> Void)
which means that sing returns "a function that returns nothing i.e. () -> Void". sing doesn't return Void, it returns another function. That returned function does whatever it needs to do, and does not return a value, hence Void.
The function being returned here is singing, which calls taylor.playSong().
You could declare sing to return Void, instead of () -> Void, but you'd also have to edit its body:
func sing() {
let taylor = Singer()
taylor.playSong()
}
Since sing can't return another function now, it can only call taylor.playSong() directly.
It doesn't return Void, it returns a closure with no arguments and no return value. This is the same as an anonymous function.

Returning after calling completion handler, or return completion handler parameter?

I have a method that can be summed up to look like this:
func apply(username: String, email: String, password: String,
onDone: #escaping (_ error: Error?) -> ())
{
//Do async stuff
do
{
try asyncGood()
onDone(nil)
return
}
catch
{
onDone(error)
return
}
}
What's the difference between doing:
return onDone(error)
versus
onDone(error)
return
?
Does it just save an extra line of code? I don't see any difference between the two. Am I missing some fundamental aspect of asynchronous Swift programming?
In your opinion, should I always try to condense everything down such that onDone(...) only gets called once at the end?
Semantically, both cases are the same. You are basically saying:
return ()
Your method is declared to return (), and since the onDone closure also returns a (), you can say return onDone(error). The return types match.
However, I find writing them in 2 separate lines more readable:
// This clearly shows that you are doing 2 separate things
onDone(error) // call the completion handler
return // return
Or even omit the return!
onDone(error)
// there will be an implicit return at the end of the method.
Both are same. apply function return type is Void and onDone closure return type is also Void. So both are same.
return onDone(error)
or
onDone(error)
return
or you can just ignore return because return type is Void
onDone(error)
There is no difference. In fact, there is no need for return keyword at all.
For swift all the following declaration is equivalent:
func doNothing() {
}
func doNothing2() -> Void {
}
func doNothing3() -> () {
}
func doNothing4() {
return ()
}
func doNothing5() -> Void {
return ()
}
When you return () you return nothing. No return is exactly the same as return nothing. Functions returning Void may be equivalently used as following
doNothing()
var result = doNothing()
More, Void can also be used as a type parameter which is a very powerful feature:
func genericCall<T>(_ f: () -> T) -> T {
return f()
}
var result1 = genericCall { print("test") } // result1 is Void
var result2 = genericCall { return 5 } // result2 is Int
Answering your initial question, I would suggest to omit return at all
func doStuffAsync(_ callback: #escaping (Error?) -> Void) {
// Just an example. Could be any other async call.
DispatchQueue.main.async {
do {
try doStuff()
callback(nil)
}
catch {
callback(error)
}
}
}

How to declare dictionary with functions as values and keyed by integer?

struct Test {
func isOk () -> Bool{
return true
}
var mapping: [Int: () -> Bool] = [
1: isOk
]
func test() -> Bool {
return mapping[1]()
}
}
I got this error:
Cannot convert value of type '(Test) -> () -> Bool' to expected dictionary value type '() -> Bool'
Any idea? Thanks!
You're seeing this weird type ((Test) -> () -> Bool) because Swift instance methods are curried functions.
If making isOk into a static method is acceptable, you can do this:
struct Test {
static func isOk() -> Bool { //Make it static
return true
}
var mapping: [Int : () -> Bool] = [
1 : Test.isOk // Static method must be qualified by its parent type
]
func test() -> Bool { //Bool return value necessary
//TODO: remove force unwrapping.
return mapping[1]!() //subscripting a Dict might return nil
}
}
If isOk must remain as an instance method, you can do this:
struct Test {
func isOk() -> Bool {
return true
}
var mapping: [Int : (Test) -> () -> Bool] = [ //change type
1 : isOk
]
//the instance method received from the dict needs an instance to act on
func test(instance: Test) -> Bool { //Bool return value necessary
//TODO: remove force unwrapping.
return mapping[1]!(instance)() //curried function call
}
}
#AMomchilov is dead right about why this is happening. At static scope (such as in mapping's default value assignment), instance methods are curried functions – meaning that they return a function that take a given instance to operate on as a parameter, rather than implicitly operating on self (as self doesn't refer to an instance at static scope).
Another potential solution to the ones already proposed if you don't wish to make isOk a static method and would like mapping to remain a [Int : () -> Bool], is to make it lazy. This will allow you to access isOk in the assignment of mapping, as it's now at instance scope – therefore meaning the function type will be () -> Bool.
struct Test {
func isOk () -> Bool {
return true
}
lazy var mapping: [Int: () -> Bool] = [
1 : self.isOk
]
mutating func test() -> Bool {
// you need to think about situations where mapping[1] returns nil
// and deal with it in a more proper way than force unwrapping
return mapping[1]!()
}
}
The only caveat to this approach is that you'll need to mark test() as mutating – as the default value of a lazy variable will be set upon accessing it for the first time, thus mutating the struct.
Although a maybe even cleaner alternative (depending on the use case), is just to assign your function to the dictionary (or the dictionary itself) in the initialiser. This will allow you to keep mapping non-lazy, therefore meaning test doesn't have to be mutating.
struct Test {
func isOk () -> Bool {
return true
}
init() {
mapping[1] = isOk
}
var mapping = [Int: () -> Bool]()
func test() -> Bool {
// you need to think about situations where mapping[1] returns nil
// and deal with it in a more proper way than force unwrapping
return mapping[1]!()
}
}
Again, this ensures that you're at instance scope when assigning the method to the dictionary, thus ensuring that it has the signature () -> Bool.

Swift function with a function as parameter

I have a question about why I get a compilation error of "Missing return in a function". I am following the examples in the "The Swift Programming Language" book and there is a section about passing a function as a parameter of another function.
Here is the book's example which compiles fine:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition (item) {// anonymous function call
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
I understand this, but I thought I could make a subtle change, because I felt the if condition(item){ } was redundant.
Here is my alteration:
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
return condition(item)
}//error here with "Missing return in a function expected to return bool"
}
I'm returning a bool because I return the result of the function. There is no case where I would not return a bool during the for-in loop.
I don't understand why this does not compile, could someone explain why?
First, your change doesn't do what the old code did. Your version returns the result of testing the first element in the list, not whether any of the elements pass the test.
The reason for the error is that your code isn't guaranteed to execute return at all. If the list is empty, then you'll drop to the end of the function without calling return. The compiler is telling you that.
func hasAnyMatches(list: [Int], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return bool
}