InnisBrendan
InnisBrendan

Reputation: 2249

Why does this compile? Function declared in another function

I made an error in my code which I thought should prevent it from compiling and running, but it didn't. So I am curious, why does this compile:

func function1() {
    print("function1")

    func function2() {
        print("function2")
    }
}

function1() // prints "function1"

Upvotes: 0

Views: 65

Answers (1)

Alexander
Alexander

Reputation: 63331

Because Swift supports nested functions.

... You can also define functions inside the bodies of other functions, known as nested functions.

Nested functions are hidden from the outside world by default, but can still be called and used by their enclosing function. An enclosing function can also return one of its nested functions to allow the nested function to be used in another scope.

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Functions.html#//apple_ref/doc/uid/TP40014097-CH10-ID178

There are many uses for them, but one common use case is to define a public function that uses a nested function to compute a result via recursion. For example, as a result of such a design, you could add parameter checking once (in the outer function), so that you don't have to repeat the checks on every call of the recursion.

func factorial(_ i: Int) -> Int {
    // This check is only done once, rather than with every recursion
    print("Checking to verify that i is non-negative")
    guard 0 <= i else {
        fatalError("Can't compute factorial of the negative number: \(i)")
    }

    func factorialRecurser(_ i: Int) -> Int {
        print("Computing factorial of \(i)")
        switch i {
            case 0, 1: return 1;
            default: return i * factorialRecurser(i - 1)
        }
    }

    return factorialRecurser(i)
}

print(factorial(10))

Results in the following output:

Checking to verify that i is non-negative
Computing factorial of 10
Computing factorial of 9
Computing factorial of 8
Computing factorial of 7
Computing factorial of 6
Computing factorial of 5
Computing factorial of 4
Computing factorial of 3
Computing factorial of 2
Computing factorial of 1
3628800

Compare this to a more naive solution:

func naiveFactorial(_ i: Int) -> Int {
    // This check is needlessly repeated
    print("Checking to verify that i is non-negative")
    guard 0 <= i else {
        fatalError("Can't compute factorial of the negative number: \(i)")
    }

    print("Computing factorial of \(i)")
    switch i {
        case 0, 1: return 1;
        default: return i * naiveFactorial(i - 1)
    }
}

print(naiveFactorial(10))

Results in the following output:

Checking to verify that i is non-negative
Computing factorial of 10
Checking to verify that i is non-negative
Computing factorial of 9
Checking to verify that i is non-negative
Computing factorial of 8
Checking to verify that i is non-negative
Computing factorial of 7
Checking to verify that i is non-negative
Computing factorial of 6
Checking to verify that i is non-negative
Computing factorial of 5
Checking to verify that i is non-negative
Computing factorial of 4
Checking to verify that i is non-negative
Computing factorial of 3
Checking to verify that i is non-negative
Computing factorial of 2
Checking to verify that i is non-negative
Computing factorial of 1
3628800

Look at how many times the non-negative check was redundantly performed.

Upvotes: 5

Related Questions