What is the benefit of nesting functions (in general/in Swift)
Asked Answered
B

3

9

I'm just learning some Swift and I've come across the section that talks about nesting functions:

Functions can be nested. Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that is long or complex.

From here

So if the purported benefit is to "organize the code", why not just have the nested function independently, outside of the outer function? That, to me, seems more organized.

The only benefit I can discern is that you "have access to variables that were declared in the outer function", but this seems trivial in comparison to the messiness of having nested functions.

Any thoughts?

Bewley answered 20/5, 2015 at 15:49 Comment(1)
Keep reading until you reach Functions chapter and then you will notice the benefit :)Toxoid
L
23

So if the purported benefit is to "organize the code", why not just have the nested function independently, outside of the outer function? That, to me, seems more organized.

Oh, I totally disagree. If the only place where the second function is needed is inside the first function, keeping it inside the first function is much more organized.

Real-life examples here: http://www.apeth.com/swiftBook/ch02.html#_function_in_function

Plus, a function in a function has the local environment in scope. Code inside the nested function can "see" local variables declared before the nested function declaration. This can be much more convenient and natural than passing a bunch of parameters.

However, the key thing that a local function lets you do that you could not readily do in any other way is that you can form the function in real time (because a function is a closure) and return it from the outer function.

http://www.apeth.com/swiftBook/ch02.html#_function_returning_function

Lollygag answered 20/5, 2015 at 15:54 Comment(3)
well, that was simple. thanks for highlighting that! (will mark correct answer in 6 minutes)Bewley
@Lollygag I totally agree with you; My only concern is the size of the function's body and the gap between parent function declaration and its actual body. Say we have 3-5 helper functions (local functions) inside a function, they need to be declared at top of the function; So the reader has to scroll many lines to reach the actual body; What do you suggest for such situations? Should we extract this function as a new class?Jellyfish
@Lollygag No, I think maybe I misexplained the case. a screenshot of an example: dropbox.com/s/lzepxqxujrth4yf/…. My concern is about the parent func which here is convertToParams & the gap between its signature & its actual body(MARK:convertToParams body) I really like to package functions that help a function to do its job inside the function; but this problem that u have to scroll from the signature to the actual body kinda makes me worrying about its readability. And It might needs even more functions. What's ur suggestion?Jellyfish
Q
2

One really nice thing is that Xcode will indent nested functions within their parent function in the function pop-up. The function popup is much easier to navigate with functions related to recalculating the layout indented and all grouped in one place.

image

Quesnay answered 23/12, 2017 at 14:39 Comment(0)
T
1

IMO, the only difference of closures and nested functions is recursion. You can refer the function itself in the function body without a trick.

func a() {
    func b() {
        b() // Infinite loop!
    }
    b()
}

Captured reference type object dies when the capturer dies. In this case, capturer is the function's lexical scope. Which means the function is going to die when it finishes its execution.

Technically, this makes a reference-cycle, and usually discouraged. But this can be useful if you use this wisely.

For example, combine this with asynchronous operations.

func spawnAsyncOp1(_ completion: @escaping () -> Void) {
    enum Continuation {
        case start
        case waitForSomethingElse1
        case retry
        case end
    }
    let someResource = SomeResource()
    func step(_ c: Continuation) {
        switch c {
        case .start:
            return step(.waitForSomethingElse1)

        case .waitForSomethingElse1:
            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10), execute: {
                let fc = (someResource.makeRandomResult() % 100 < 50) ? .end : .retry as Continuation
                print("\(fc)")
                return step(fc)
            })

        case .retry:
            return step(.start)

        case .end:
            return completion()
        }
    }
    return step(.start)
}

It can make resource management in a coroutine execution simpler without an explicit object instance. Resources are simply captured in function spawnAsyncOp1 and will be released when the function dies.

Tifanie answered 6/5, 2017 at 17:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.