Home > Enterprise >  swift recursion: function vs closure
swift recursion: function vs closure

Time:10-22

This compiles:

let badger = get_closure()

func get_closure() -> (Int) -> Void {
  return { (x: Int) -> Void in
    print(x)
    if x > 4 {
      return
    } else {
      badger(x   1)
    }
  }
}

badger(1)

This doesn't with circular reference errors:

let badger = get_closure()

let get_closure = { () -> (Int) -> Void in
  return { (x: Int) -> Void in
    print(x)
    if x > 4 {
      return
    } else {
      badger(x   1)
    }
  }
}

badger(1)

Why? I thought the func syntax is just sugar for the second more explicit syntax.

CodePudding user response:

It's hard to understand where this approach should be used, but if you really want to, then try this:

let get_closure: (Int) -> Void = { x in
      print(x)
    if x > 4 {
      return
    } else {
      badger(x   1)
    }
}

let badger = get_closure

badger(1)

The order of initialization of dependent variables is usually important. If you assign a value after the closure, there will be no error:

let get_closure = { () -> (Int) -> Void in
  return { (x: Int) -> Void in
    print(x)
    if x > 4 {
      return
    } else {
      badger(x   1)
    }
  }
}

let badger = get_closure()

badger(1)

CodePudding user response:

The fact that the first one works is a long-standing bug/quirk in the compiler, not an intended feature. It can lead to undefined (or at least unexpected) behaviors. You can demonstrate that it's a quirk of top-level declarations by wrapping your working code in a function and see that it fails very similarly to your second example:

func f() {
    let badger = get_closure()

    func get_closure() -> (Int) -> Void {  // ERROR: Closure captures 'badger' before it is declared
        return { (x: Int) -> Void in
            print(x)
            if x > 4 {
                return
            } else {
                badger(x   1)
            }
        }
    }

    badger(1)
}

The key feature of top-level global declarations like badger is that they're lazily evaluated, so the assignment actually happens after the declaration of the function. In your second example, both variables are lazy, so the order comes back in. But it's dangerous to rely on this fact, as discussed in the linked forum posts.

When exploring subtle Swift behavior, I always recommend building a commandline app and placing your test code inside of a function to eliminate the weirdness of both Playgrounds and top-level executable code. (Playgrounds isn't relevant in this case, but it comes up a lot.)

  • Related