Snippets

Alex Lynch Capture Lists for Local Functions

Created by Alex Lynch last modified

Capture Lists for Local Functions

  • Proposal: SE-00XX
  • Authors: Alex Lynch
  • Review Manager: TBD
  • Status: Awaiting implementation

Introduction

This proposal adds capture list semantics to local functions identical to those available for closures.

Swift-evolution thread: Making capturing semantics of local

Motivation

There is a feature disparity in swift 4 between local functions and closures (here known collectively as subroutines). In workflows that rely heavily on subroutines the difference between the two with regard to capture semantics is usually discovered first by painful mistake. Thereafter the programmer must make his choice between the two forms not based purely on expressiveness, or even style, but often perforce, based on the requirement to hold references other-than-strongly.

Example:

class A {
    func regularFunc() {
        func subrutineWithStrongRef() {
            self.bar()
        }

        let subrutineWithWeakRef = { [weak self] () -> Void in
            if let this = self {
                self?.bar()
            }
        }
    }

    ...
}

In the example above, the programmer intends to hold strong reference to self in subrutineWithStrongRef. This subrutine could be expressed either with a local function or a closure. However in subrutineWithWeakRef the programmer intends to hold a weak reference to self. At present, the programer has no choice but to select a closure, even if the use case would make a local function more expressive or easier to read.

Proposed solution

This proposal adds capture list syntax to local functions. The above example would thus be rewritten as:

class A {
    func regularFunc() {
        func subrutineWithStrongRef() {
            self.bar()
        }

        func subrutineWithWeakRef() -> Void { [weak self] in
            if let this = self {
                self?.bar()
            }
        }
    }

    ...
}

Detailed design

The existing grammar for capture lists is to be reused as an optional addition to the local function grammar. This is expected to be merely a syntactic addition which is compiled into the same form as capture lists for closures.

Source compatibility

The proposed feature follows the standard of progressive disclosure within swift. All Swift 3 and Swift 4 code will continue to compile and run as expected. No migration is needed.

Effect on ABI stability

The proposed change will have little to no effect on the ABI.

-- Real talk; I'm not sure yet if there will be any change. The compiler supports capture lists for closures, but does it handle closures and local functions differently in any meaningful way? Not sure yet. This section will be rewritten after the reference implementation of the feature.

Effect on API resilience

The proposed feature by definition has no effect on API. It is a modification to local functions which are visible only in the containing function scope.

Alternatives considered

Alternatives to the proposed solution are twofold. The first is to do nothing and live with the current feature disparity between local functions and closures. The second is alternative syntax for local function capture lists. The following forms were considered:

class A {
    func regularFunc() {        
        // capture list before the curly brace
        func subrutineWithWeakRef() -> Void [weak self] { 
            if let this = self {
                self?.bar()
            }
        }
        // capture list after curly brace but without 'in'
        func subrutineWithWeakRef() -> Void { [weak self]
            if let this = self {
                self?.bar()
            }
        }
    }
    ...
}

The first form was rejected as introducing a wholly new syntactical form at no benefit in clarity.

The second form was rejected as a confusing deviation from what is otherwise an immediately familiar adoption of closure capture list syntax.

Comments (0)