[Draft Proposal] A simplified notation for avoiding the weak/strong dance with closure capture lists


(E. Maloney) #1

Hello,

This draft proposal is a follow-up to the previous threads: "Wanted: syntactic sugar for [weak self] callbacks" and "Allowing guard let self = self else { … } for weakly captured self in a closure."

A gist for this draft exists here <https://gist.github.com/emaloney/d34ac9b134ece7c60440>.

Comments welcome!

E.

···

---

Simplified notation for avoiding the [weak self]/strongSelf dance with closures

Proposal: TBD
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#introduction>Introduction

Frequently, closures are used as completion callbacks for asynchronous operations, such as when dealing with network requests. It is quite common to model these sorts of operations in such a way that an object instance represents a request/response transaction, for example:

protocol NetworkTransaction: class
{
    enum Result {
        case Succeeded(NSData)
        case Failed(ErrorType)
    }

    func execute(completion: (Result) -> Void)
}
Here, the NetworkTransaction protocol declares the interface by which an asynchronous transaction occurs. The user of a NetworkTransaction calls the execute() function, passing in a completion function that is called at some time in the future, when the transaction completes.

For example, imagine a hypothetical DataConsumer class that uses a transaction to try to fetch some network data and process it:

class DataConsumer
{
    let transaction: NetworkTransaction

    init(transaction: NetworkTransaction)
    {
        self.transaction = transaction
    }

    func fetchData()
    {
        transaction.execute() { [weak self] result in
            guard let strongSelf = self else { return }

            switch result {
            case .Succeeded(let data):
                strongSelf.processData(data)

            case .Failed(let err):
                strongSelf.handleError(err)
            }
        }
    }

    func processData(data: NSData)
    {
        // process the data
    }

    func handleError(error: ErrorType)
    {
        // handle the error
    }
}
You'll notice the [weak self]/strongSelf dance in the fetchData() function. This is a common pattern with asynchronously-executed closures, and it signals the possibility that a closure might outlive its usefulness.

Because the NetworkTransaction may complete at any time, it is possible that the closure will execute after the DataConsumer that initiated the transaction has been deallocated. Perhaps the user has navigated elsewhere in the application and whatever data was to be fetched by DataConsumer is no longer needed.

In this case, after a DataConsumer instance goes away, we don't really want the closure doing anything. So, we capture self weakly to ensure that the closure doesn't hold a reference to the owning DataConsumer. That prevents a reference cycle and ensures that DataConsumer can be deallocated when no longer in use.

When it comes time to execute the closure, the guard statement effectively asks the question, "Is self still alive?" If the answer is no, the guard forces a return and the rest of the closure does not execute.

If self is still alive, then the weakly-captured self will be non-nil and it will be converted into a strong reference held by strongSelf for the duration of the closure's execution.

When the closure is done executing, strongSelf goes away, once again making the DataConsumer eligible for deallocation when no other references are held.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#the-problem>The Problem

The [weak self]/strongSelf dance requires common boilerplate wherever it is used, and the fact that a self-like variable with an arbitrary name adds noise within the closure. The more strongSelf is needed within the closure, the more noise there is.

Further, using a consistent name like strongSelf is by convention only; it can't be enforced by the compiler, so searching your codebase for a given keyword won't be exhaustive if team members use different names.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#proposed-solution>Proposed Solution

The proposed solution adds a new capture type by repurposing the guard keyword for another use, which would look like:

transaction.execute() { [guard self] result in
    switch result {
    case .Succeeded(let data):
        self.processData(data)

    case .Failed(let err):
        self.handleError(err)
    }
}
Here, the [guard self] capture list serves as a signal that the compiler should handle the weak/strong dance itself. When encountering [guard self], the compiler should emit code that does the following:

Captures self in a weak reference on behalf of the closure
Whenever the closure is about to be executed, the weak reference is checked to see if self is still alive
If self is not alive, the closure returns immediately and nothing within the braces is executed
If self is alive, it is upgraded to a strong reference for the lifetime of the closure's execution. Within the closure, self is non-optional, unlike how it would be with a [weak self] capture. When the closure is done executing, the strong reference will be cleared and only the weak reference will be held on behalf of the closure.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#closures-with-return-values>Closures with Return Values

For closures with a Void return type, the notation is simple, because no explicit value is expected as a result of executing the closure.

Closures with a return value will require an else clause:

let pollHappiness: () -> Bool = { [guard self else false] in
    return self.isHealthy && !self.isHungry && !self.isFearful && self.hasLove
}
Here, the else clause provides a value to return in cases where self has gone away and the guard fails.

In this example, if you call pollHappiness() after self has been deallocated, false will always be returned.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#capturing-other-references>Capturing Other References

This notation is not limited to handling self. These capture lists are valid, too:

let capturingTwo = { [guard self, button] in
    // weakly capture self and button
    // but execute the closure with strong references
    // if and only if self and button still exist
    // when the closure is about to execute
}

let captureWithReturn: () -> UIView = {
    [guard button, window = button!.window else nil] in ...

    // window is captured and stored weakly at the time the
    // closure declaration is encountered during execution;
    // button is guaranteed to be non-nil at that time, but
    // it may go away later, so we guard on button too
}
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#caveats>Caveats

This notation is not intended to be a full-fledged replacement for guard statements within the closure. We are only using guard here as a way to declare a specific memory-management behavior for references.

As a result, we are not attempting to support a where clause or boolean expressions within this notation.

Rather, we're simply adding a new capture behavior and providing a means to specify an early exit if the behavior couldn't be fulfilled because one or more of the required objects was deallocated before the closure was executed.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternatives-considered>Alternatives Considered

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#status-quo>Status Quo

The primary alternative is to do nothing, requiring developers to add boilerplate guard code and handle upgrading the weak-to-strong references manually.

As stated above, this leads to needless boilerplate that can easily be factored out by the compiler. Also, the use of a self-like variable with an arbitrary name makes it more difficult to exhaustively find such uses in large projects. With this proposal, searching for the text "[guard" is all that's necessary to find all instances of this memory management technique.

Finally, the need to declare and use alternate names to capture values that already have existing names adds visual clutter to code and serves to obscure the code's original intent, making it harder to reason about.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternate-notations>Alternate Notations

The notation proposed above was chosen for concision. Two variations were considered, but rejected, because they add words to the statements without adding meaning.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-weak-after-guard>Requiring weak after guard

This alternate notation used a weak keyword after the guard keyword in the capture list, eg. [guard weak self] instead of [guard self].

The use of the weak keyword did not modify behavior in any way, so it was removed from the proposal to be less verbose.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-return-after-else>Requiring return after else

A longer notation requiring the return keyword for closures specifying return values was considered, but rejected, for similar reasons.

Such a notation would make [guard self else nil] look like [guard self else return nil].

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-both>Requiring both

If both keywords above were required, the verbiage would become quite unwieldy in the closure's capture list, which often appears on the same line after the opening parenthesis, which itself is often indented:

let toggleHappyButton: () -> Bool? = { [guard weak self, button else return nil] in
    let state = button.highlighted
    button.highlighted = self.pollHappiness()
    return state
}
That seems longer than necessary.

For the reasons above, the two keywords above were not included in this proposal.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#citations>Citations

Variations on this proposal were discussed earlier in the swift-evolution threads "Wanted: syntactic sugar for [weak self] callbacks <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html>" and "Allowing guard let self = self else { … } for weakly captured self in a closure. <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009023.html>".


Guarded closures
#2

I much prefer being allowed to re-bind self with `if let self = self { … }` or `guard let self = self else { … }`. This would not introduce any new syntax, be useful in a much broader range of situations than the narrow use cases of `[guard self]`, `[guard self else …]` etc.

···

Le 6 févr. 2016 à 06:17, Evan Maloney via swift-evolution <swift-evolution@swift.org> a écrit :

Hello,

This draft proposal is a follow-up to the previous threads: "Wanted: syntactic sugar for [weak self] callbacks" and "Allowing guard let self = self else { … } for weakly captured self in a closure."

A gist for this draft exists here <https://gist.github.com/emaloney/d34ac9b134ece7c60440>.

Comments welcome!

E.

---

Simplified notation for avoiding the [weak self]/strongSelf dance with closures

Proposal: TBD
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#introduction>Introduction

Frequently, closures are used as completion callbacks for asynchronous operations, such as when dealing with network requests. It is quite common to model these sorts of operations in such a way that an object instance represents a request/response transaction, for example:

protocol NetworkTransaction: class
{
    enum Result {
        case Succeeded(NSData)
        case Failed(ErrorType)
    }

    func execute(completion: (Result) -> Void)
}
Here, the NetworkTransaction protocol declares the interface by which an asynchronous transaction occurs. The user of a NetworkTransaction calls the execute() function, passing in a completion function that is called at some time in the future, when the transaction completes.

For example, imagine a hypothetical DataConsumer class that uses a transaction to try to fetch some network data and process it:

class DataConsumer
{
    let transaction: NetworkTransaction

    init(transaction: NetworkTransaction)
    {
        self.transaction = transaction
    }

    func fetchData()
    {
        transaction.execute() { [weak self] result in
            guard let strongSelf = self else { return }

            switch result {
            case .Succeeded(let data):
                strongSelf.processData(data)

            case .Failed(let err):
                strongSelf.handleError(err)
            }
        }
    }

    func processData(data: NSData)
    {
        // process the data
    }

    func handleError(error: ErrorType)
    {
        // handle the error
    }
}
You'll notice the [weak self]/strongSelf dance in the fetchData() function. This is a common pattern with asynchronously-executed closures, and it signals the possibility that a closure might outlive its usefulness.

Because the NetworkTransaction may complete at any time, it is possible that the closure will execute after the DataConsumer that initiated the transaction has been deallocated. Perhaps the user has navigated elsewhere in the application and whatever data was to be fetched by DataConsumer is no longer needed.

In this case, after a DataConsumer instance goes away, we don't really want the closure doing anything. So, we capture self weakly to ensure that the closure doesn't hold a reference to the owning DataConsumer. That prevents a reference cycle and ensures that DataConsumer can be deallocated when no longer in use.

When it comes time to execute the closure, the guard statement effectively asks the question, "Is self still alive?" If the answer is no, the guard forces a return and the rest of the closure does not execute.

If self is still alive, then the weakly-captured self will be non-nil and it will be converted into a strong reference held by strongSelf for the duration of the closure's execution.

When the closure is done executing, strongSelf goes away, once again making the DataConsumer eligible for deallocation when no other references are held.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#the-problem>The Problem

The [weak self]/strongSelf dance requires common boilerplate wherever it is used, and the fact that a self-like variable with an arbitrary name adds noise within the closure. The more strongSelf is needed within the closure, the more noise there is.

Further, using a consistent name like strongSelf is by convention only; it can't be enforced by the compiler, so searching your codebase for a given keyword won't be exhaustive if team members use different names.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#proposed-solution>Proposed Solution

The proposed solution adds a new capture type by repurposing the guard keyword for another use, which would look like:

transaction.execute() { [guard self] result in
    switch result {
    case .Succeeded(let data):
        self.processData(data)

    case .Failed(let err):
        self.handleError(err)
    }
}
Here, the [guard self] capture list serves as a signal that the compiler should handle the weak/strong dance itself. When encountering [guard self], the compiler should emit code that does the following:

Captures self in a weak reference on behalf of the closure
Whenever the closure is about to be executed, the weak reference is checked to see if self is still alive
If self is not alive, the closure returns immediately and nothing within the braces is executed
If self is alive, it is upgraded to a strong reference for the lifetime of the closure's execution. Within the closure, self is non-optional, unlike how it would be with a [weak self] capture. When the closure is done executing, the strong reference will be cleared and only the weak reference will be held on behalf of the closure.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#closures-with-return-values>Closures with Return Values

For closures with a Void return type, the notation is simple, because no explicit value is expected as a result of executing the closure.

Closures with a return value will require an else clause:

let pollHappiness: () -> Bool = { [guard self else false] in
    return self.isHealthy && !self.isHungry && !self.isFearful && self.hasLove
}
Here, the else clause provides a value to return in cases where self has gone away and the guard fails.

In this example, if you call pollHappiness() after self has been deallocated, false will always be returned.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#capturing-other-references>Capturing Other References

This notation is not limited to handling self. These capture lists are valid, too:

let capturingTwo = { [guard self, button] in
    // weakly capture self and button
    // but execute the closure with strong references
    // if and only if self and button still exist
    // when the closure is about to execute
}

let captureWithReturn: () -> UIView = {
    [guard button, window = button!.window else nil] in ...

    // window is captured and stored weakly at the time the
    // closure declaration is encountered during execution;
    // button is guaranteed to be non-nil at that time, but
    // it may go away later, so we guard on button too
}
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#caveats>Caveats

This notation is not intended to be a full-fledged replacement for guard statements within the closure. We are only using guard here as a way to declare a specific memory-management behavior for references.

As a result, we are not attempting to support a where clause or boolean expressions within this notation.

Rather, we're simply adding a new capture behavior and providing a means to specify an early exit if the behavior couldn't be fulfilled because one or more of the required objects was deallocated before the closure was executed.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternatives-considered>Alternatives Considered

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#status-quo>Status Quo

The primary alternative is to do nothing, requiring developers to add boilerplate guard code and handle upgrading the weak-to-strong references manually.

As stated above, this leads to needless boilerplate that can easily be factored out by the compiler. Also, the use of a self-like variable with an arbitrary name makes it more difficult to exhaustively find such uses in large projects. With this proposal, searching for the text "[guard" is all that's necessary to find all instances of this memory management technique.

Finally, the need to declare and use alternate names to capture values that already have existing names adds visual clutter to code and serves to obscure the code's original intent, making it harder to reason about.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternate-notations>Alternate Notations

The notation proposed above was chosen for concision. Two variations were considered, but rejected, because they add words to the statements without adding meaning.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-weak-after-guard>Requiring weak after guard

This alternate notation used a weak keyword after the guard keyword in the capture list, eg. [guard weak self] instead of [guard self].

The use of the weak keyword did not modify behavior in any way, so it was removed from the proposal to be less verbose.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-return-after-else>Requiring return after else

A longer notation requiring the return keyword for closures specifying return values was considered, but rejected, for similar reasons.

Such a notation would make [guard self else nil] look like [guard self else return nil].

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-both>Requiring both

If both keywords above were required, the verbiage would become quite unwieldy in the closure's capture list, which often appears on the same line after the opening parenthesis, which itself is often indented:

let toggleHappyButton: () -> Bool? = { [guard weak self, button else return nil] in
    let state = button.highlighted
    button.highlighted = self.pollHappiness()
    return state
}
That seems longer than necessary.

For the reasons above, the two keywords above were not included in this proposal.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#citations>Citations

Variations on this proposal were discussed earlier in the swift-evolution threads "Wanted: syntactic sugar for [weak self] callbacks <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html>" and "Allowing guard let self = self else { … } for weakly captured self in a closure. <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009023.html>".
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Eugene Gubin) #3

I liked this proposal until the capture list took another resposibility to
be a fast path for a closure :slight_smile:
Is it such common situation when we want return value in any case without
ensuring lifetime of closure' context? For me if closure depends on
anything which is deallocated, then result could not be computed so it is
'nil'. Caller side could decide what to do with that 'nil'.

···

2016-02-06 8:17 GMT+03:00 Evan Maloney via swift-evolution < swift-evolution@swift.org>:

Hello,

This draft proposal is a follow-up to the previous threads: "Wanted:
syntactic sugar for [weak self] callbacks" and "Allowing guard let self =
self else { … } for weakly captured self in a closure."

A gist for this draft exists here
<https://gist.github.com/emaloney/d34ac9b134ece7c60440>.

Comments welcome!

E.

---

Simplified notation for avoiding the [weak self]/strongSelf dance with
closures

   - Proposal: TBD
   - Author: Evan Maloney <https://github.com/emaloney>
   - Status: *Draft*
   - Review manager: TBD

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#introduction>
Introduction

Frequently, closures are used as completion callbacks for asynchronous
operations, such as when dealing with network requests. It is quite common
to model these sorts of operations in such a way that an object instance
represents a request/response transaction, for example:

protocol NetworkTransaction: class
{
    enum Result {
        case Succeeded(NSData)
        case Failed(ErrorType)
    }

    func execute(completion: (Result) -> Void)
}

Here, the NetworkTransaction protocol declares the interface by which an
asynchronous transaction occurs. The user of a NetworkTransaction calls
the execute() function, passing in a completion function that is called
at some time in the future, when the transaction completes.

For example, imagine a hypothetical DataConsumer class that uses a
transaction to try to fetch some network data and process it:

class DataConsumer
{
    let transaction: NetworkTransaction

    init(transaction: NetworkTransaction)
    {
        self.transaction = transaction
    }

    func fetchData()
    {
        transaction.execute() { [weak self] result in
            guard let strongSelf = self else { return }

            switch result {
            case .Succeeded(let data):
                strongSelf.processData(data)

            case .Failed(let err):
                strongSelf.handleError(err)
            }
        }
    }

    func processData(data: NSData)
    {
        // process the data
    }

    func handleError(error: ErrorType)
    {
        // handle the error
    }
}

You'll notice the [weak self]/strongSelf dance in the fetchData() function.
This is a common pattern with asynchronously-executed closures, and it
signals the possibility that a closure might outlive its usefulness.

Because the NetworkTransaction may complete at any time, it is possible
that the closure will execute after the DataConsumer that initiated the
transaction has been deallocated. Perhaps the user has navigated elsewhere
in the application and whatever data was to be fetched by DataConsumer is
no longer needed.

In this case, after a DataConsumer instance goes away, we don't really
want the closure doing anything. So, we capture self weakly to ensure
that the closure doesn't hold a reference to the owning DataConsumer.
That prevents a reference cycle and ensures that DataConsumer can be
deallocated when no longer in use.

When it comes time to execute the closure, the guard statement
effectively asks the question, "Is self still alive?" If the answer is
no, the guard forces a return and the rest of the closure does not execute.

If self *is* still alive, then the weakly-captured self will be non-nil and
it will be converted into a strong reference held by strongSelf for the
duration of the closure's execution.

When the closure is done executing, strongSelf goes away, once again
making the DataConsumer eligible for deallocation when no other
references are held.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#the-problem>The
Problem

The [weak self]/strongSelf dance requires common boilerplate wherever it
is used, and the fact that a self-like variable with an arbitrary name
adds noise within the closure. The more strongSelf is needed within the
closure, the more noise there is.

Further, using a consistent name like strongSelf is by convention only;
it can't be enforced by the compiler, so searching your codebase for a
given keyword won't be exhaustive if team members use different names.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#proposed-solution>Proposed
Solution

The proposed solution adds a new capture type by repurposing the guard keyword
for another use, which would look like:

transaction.execute() { [guard self] result in
    switch result {
    case .Succeeded(let data):
        self.processData(data)

    case .Failed(let err):
        self.handleError(err)
    }
}

Here, the [guard self] capture list serves as a signal that the compiler
should handle the weak/strong dance itself. When encountering [guard self],
the compiler should emit code that does the following:

   - Captures self in a weak reference on behalf of the closure
   - Whenever the closure is about to be executed, the weak reference is
   checked to see if self is still alive
      - If self is not alive, the closure returns immediately and nothing
      within the braces is executed
      - If self is alive, it is upgraded to a strong reference for the
      lifetime of the closure's execution. Within the closure, self is
      non-optional, unlike how it would be with a [weak self] capture.
      When the closure is done executing, the strong reference will be cleared
      and only the weak reference will be held on behalf of the closure.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#closures-with-return-values>Closures
with Return Values

For closures with a Void return type, the notation is simple, because no
explicit value is expected as a result of executing the closure.

Closures with a return value will require an else clause:

let pollHappiness: () -> Bool = { [guard self else false] in
    return self.isHealthy && !self.isHungry && !self.isFearful && self.hasLove
}

Here, the else clause provides a value to return in cases where self has
gone away and the guard fails.

In this example, if you call pollHappiness() after self has been
deallocated, false will always be returned.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#capturing-other-references>Capturing
Other References

This notation is not limited to handling self. These capture lists are
valid, too:

let capturingTwo = { [guard self, button] in
    // weakly capture self and button
    // but execute the closure with strong references
    // if and only if self and button still exist
    // when the closure is about to execute
}
let captureWithReturn: () -> UIView = {
    [guard button, window = button!.window else nil] in ...

    // window is captured and stored weakly at the time the
    // closure declaration is encountered during execution;
    // button is guaranteed to be non-nil at that time, but
    // it may go away later, so we guard on button too
}

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#caveats>Caveats

This notation is not intended to be a full-fledged replacement for guard
statements within the closure. We are only using guard here as a way to
declare a specific memory-management behavior for references.

As a result, we are not attempting to support a where clause or boolean
expressions within this notation.

Rather, we're simply adding a new capture behavior and providing a means
to specify an early exit if the behavior couldn't be fulfilled because one
or more of the required objects was deallocated before the closure was
executed.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#impact-on-existing-code>Impact
on Existing Code

None, since this does not affect any existing constructs. Implementation
of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternatives-considered>Alternatives
Considered
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#status-quo>Status
Quo

The primary alternative is to do nothing, requiring developers to add
boilerplate guard code and handle upgrading the weak-to-strong references
manually.

As stated above, this leads to needless boilerplate that can easily be
factored out by the compiler. Also, the use of a self-like variable with
an arbitrary name makes it more difficult to exhaustively find such uses in
large projects. With this proposal, searching for the text "[guard" is
all that's necessary to find all instances of this memory management
technique.

Finally, the need to declare and use alternate names to capture values
that already have existing names adds visual clutter to code and serves to
obscure the code's original intent, making it harder to reason about.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternate-notations>Alternate
Notations

The notation proposed above was chosen for concision. Two variations were
considered, but rejected, because they add words to the statements without
adding meaning.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-weak-after-guard>
Requiring weak after guard

This alternate notation used a weak keyword after the guard keyword in
the capture list, eg. [guard weak self] instead of [guard self].

The use of the weak keyword did not modify behavior in any way, so it was
removed from the proposal to be less verbose.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-return-after-else>
Requiring return after else

A longer notation requiring the return keyword for closures specifying
return values was considered, but rejected, for similar reasons.

Such a notation would make [guard self else nil] look like [guard self
else return nil].
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-both>Requiring
both

If both keywords above were required, the verbiage would become quite
unwieldy in the closure's capture list, which often appears on the same
line after the opening parenthesis, which itself is often indented:

let toggleHappyButton: () -> Bool? = { [guard weak self, button else return nil] in
    let state = button.highlighted
    button.highlighted = self.pollHappiness()
    return state
}

That seems longer than necessary.

For the reasons above, the two keywords above were not included in this
proposal.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#citations>Citations
Variations on this proposal were discussed earlier in the swift-evolution
threads "Wanted: syntactic sugar for [weak self] callbacks
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html>"
and "Allowing guard let self = self else { … } for weakly captured self
in a closure.
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009023.html>
".

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kenny Leung) #4

By the time you’ve written something like this:

[guard button, window = button!.window else nil]

your shortcut has turned into a longcut.

I would prefer something a lot simpler syntactically that would handle 80% of the cases. For the rest of the cases that don’t fit, it’s probably cleaner and more understandable to just write regular guard statements.

I propose

doit { [weak self, other]? in
    doing some stuff...
}

If any of the capture list evaluates to nil, the block is not executed. If the block signature has an optional return value, nil is returned. Anything else, you’re on your own.

-Kenny

···

On Feb 5, 2016, at 9:17 PM, Evan Maloney via swift-evolution <swift-evolution@swift.org> wrote:

Hello,

This draft proposal is a follow-up to the previous threads: "Wanted: syntactic sugar for [weak self] callbacks" and "Allowing guard let self = self else { … } for weakly captured self in a closure."

A gist for this draft exists here.

Comments welcome!

E.

---

Simplified notation for avoiding the [weak self]/strongSelf dance with closures

  • Proposal: TBD
  • Author: Evan Maloney
  • Status: Draft
  • Review manager: TBD
Introduction

Frequently, closures are used as completion callbacks for asynchronous operations, such as when dealing with network requests. It is quite common to model these sorts of operations in such a way that an object instance represents a request/response transaction, for example:

protocol NetworkTransaction: class

{
    
enum
Result {
        
case
Succeeded(NSData)
        
case Failed(ErrorType
)
    }

func execute(completion: (Result) -> Void
)
}

Here, the NetworkTransaction protocol declares the interface by which an asynchronous transaction occurs. The user of a NetworkTransaction calls the execute() function, passing in a completion function that is called at some time in the future, when the transaction completes.

For example, imagine a hypothetical DataConsumer class that uses a transaction to try to fetch some network data and process it:

class
DataConsumer
{
    
let
transaction: NetworkTransaction

init
(transaction: NetworkTransaction)
    {
        
self.transaction =
transaction
    }

func fetchData
()
    {
        transaction
.execute() { [weak self] result in

guard let strongSelf = self else { return
}

switch
result {
            
case .Succeeded(let
data):
                strongSelf
.
processData(data)

case .Failed(let
err):
                strongSelf
.
handleError(err)
            }
        }
    }

func processData
(data: NSData)
    {
        
// process the data

    }

func handleError(error: ErrorType
)
    {
        
// handle the error

    }
}

You'll notice the [weak self]/strongSelf dance in the fetchData() function. This is a common pattern with asynchronously-executed closures, and it signals the possibility that a closure might outlive its usefulness.

Because the NetworkTransaction may complete at any time, it is possible that the closure will execute after the DataConsumer that initiated the transaction has been deallocated. Perhaps the user has navigated elsewhere in the application and whatever data was to be fetched by DataConsumer is no longer needed.

In this case, after a DataConsumer instance goes away, we don't really want the closure doing anything. So, we capture self weakly to ensure that the closure doesn't hold a reference to the owning DataConsumer. That prevents a reference cycle and ensures that DataConsumer can be deallocated when no longer in use.

When it comes time to execute the closure, the guard statement effectively asks the question, "Is self still alive?" If the answer is no, the guard forces a return and the rest of the closure does not execute.

If self is still alive, then the weakly-captured self will be non-nil and it will be converted into a strong reference held by strongSelf for the duration of the closure's execution.

When the closure is done executing, strongSelf goes away, once again making the DataConsumer eligible for deallocation when no other references are held.

The Problem

The [weak self]/strongSelf dance requires common boilerplate wherever it is used, and the fact that a self-like variable with an arbitrary name adds noise within the closure. The more strongSelf is needed within the closure, the more noise there is.

Further, using a consistent name like strongSelf is by convention only; it can't be enforced by the compiler, so searching your codebase for a given keyword won't be exhaustive if team members use different names.

Proposed Solution

The proposed solution adds a new capture type by repurposing the guard keyword for another use, which would look like:

transaction.execute() { [guard self] result in

switch
result {
    
case .Succeeded(let
data):
        
self.
processData(data)

case .Failed(let
err):
        
self.
handleError(err)
    }
}

Here, the [guard self] capture list serves as a signal that the compiler should handle the weak/strong dance itself. When encountering [guard self], the compiler should emit code that does the following:

  • Captures self in a weak reference on behalf of the closure
  • Whenever the closure is about to be executed, the weak reference is checked to see if self is still alive
    • If self is not alive, the closure returns immediately and nothing within the braces is executed
    • If self is alive, it is upgraded to a strong reference for the lifetime of the closure's execution. Within the closure, self is non-optional, unlike how it would be with a [weak self] capture. When the closure is done executing, the strong reference will be cleared and only the weak reference will be held on behalf of the closure.
Closures with Return Values

For closures with a Void return type, the notation is simple, because no explicit value is expected as a result of executing the closure.

Closures with a return value will require an else clause:

let pollHappiness: () -> Bool = { [guard self else false] in

return self.isHealthy && !self.isHungry && !self.isFearful && self.
hasLove
}

Here, the else clause provides a value to return in cases where self has gone away and the guard fails.

In this example, if you call pollHappiness() after self has been deallocated, false will always be returned.

Capturing Other References

This notation is not limited to handling self. These capture lists are valid, too:

let capturingTwo = { [guard self, button] in

// weakly capture self and button

// but execute the closure with strong references

// if and only if self and button still exist

// when the closure is about to execute

}

let captureWithReturn: () -> UIView =
{
    [
guard button, window = button!.window else nil] in ...

// window is captured and stored weakly at the time the

// closure declaration is encountered during execution;

// button is guaranteed to be non-nil at that time, but

// it may go away later, so we guard on button too

}

Caveats

This notation is not intended to be a full-fledged replacement for guard statements within the closure. We are only using guard here as a way to declare a specific memory-management behavior for references.

As a result, we are not attempting to support a where clause or boolean expressions within this notation.

Rather, we're simply adding a new capture behavior and providing a means to specify an early exit if the behavior couldn't be fulfilled because one or more of the required objects was deallocated before the closure was executed.

Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

Alternatives Considered

Status Quo

The primary alternative is to do nothing, requiring developers to add boilerplate guard code and handle upgrading the weak-to-strong references manually.

As stated above, this leads to needless boilerplate that can easily be factored out by the compiler. Also, the use of a self-like variable with an arbitrary name makes it more difficult to exhaustively find such uses in large projects. With this proposal, searching for the text "[guard" is all that's necessary to find all instances of this memory management technique.

Finally, the need to declare and use alternate names to capture values that already have existing names adds visual clutter to code and serves to obscure the code's original intent, making it harder to reason about.

Alternate Notations

The notation proposed above was chosen for concision. Two variations were considered, but rejected, because they add words to the statements without adding meaning.

Requiring weak after guard

This alternate notation used a weak keyword after the guard keyword in the capture list, eg. [guard weak self] instead of [guard self].

The use of the weak keyword did not modify behavior in any way, so it was removed from the proposal to be less verbose.

Requiring return after else

A longer notation requiring the return keyword for closures specifying return values was considered, but rejected, for similar reasons.

Such a notation would make [guard self else nil] look like [guard self else return nil].

Requiring both

If both keywords above were required, the verbiage would become quite unwieldy in the closure's capture list, which often appears on the same line after the opening parenthesis, which itself is often indented:

let toggleHappyButton: () -> Bool? = { [guard weak self, button else return nil] in

let state = button.
highlighted
    button
.highlighted = self.
pollHappiness()
    
return
state
}

That seems longer than necessary.

For the reasons above, the two keywords above were not included in this proposal.

Citations

Variations on this proposal were discussed earlier in the swift-evolution threads "Wanted: syntactic sugar for [weak self] callbacks" and "Allowing guard let self = self else { … } for weakly captured self in a closure.".
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Taras Zakharko) #5

I agree with Gwendal. The problem this proposal attempts to address is significant and important, but I don’t like the fact that the solution is even more situational syntax. Using an explicit guard statement is not that much more difficult, has clear semantics and is much more flexible (e.g. also in terms of return values). So yes, the best solution IMO would be if one could “redefine" self in this particular context. Its still “magic”, but its quite reasonable magic. In that sense, the original suggestion by Hoon H. is what would get my vote.

In principle, a more general solution would be provided by closure contracts/preconditions. But that is what the guard statement does already, in essence, so why not use it?

— Taras

···

On 06 Feb 2016, at 11:48, Gwendal Roué via swift-evolution <swift-evolution@swift.org> wrote:

I much prefer being allowed to re-bind self with `if let self = self { … }` or `guard let self = self else { … }`. This would not introduce any new syntax, be useful in a much broader range of situations than the narrow use cases of `[guard self]`, `[guard self else …]` etc.

Le 6 févr. 2016 à 06:17, Evan Maloney via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Hello,

This draft proposal is a follow-up to the previous threads: "Wanted: syntactic sugar for [weak self] callbacks" and "Allowing guard let self = self else { … } for weakly captured self in a closure."

A gist for this draft exists here <https://gist.github.com/emaloney/d34ac9b134ece7c60440>.

Comments welcome!

E.

---

Simplified notation for avoiding the [weak self]/strongSelf dance with closures

Proposal: TBD
Author: Evan Maloney <https://github.com/emaloney>
Status: Draft
Review manager: TBD
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#introduction>Introduction

Frequently, closures are used as completion callbacks for asynchronous operations, such as when dealing with network requests. It is quite common to model these sorts of operations in such a way that an object instance represents a request/response transaction, for example:

protocol NetworkTransaction: class
{
    enum Result {
        case Succeeded(NSData)
        case Failed(ErrorType)
    }

    func execute(completion: (Result) -> Void)
}
Here, the NetworkTransaction protocol declares the interface by which an asynchronous transaction occurs. The user of a NetworkTransaction calls the execute() function, passing in a completion function that is called at some time in the future, when the transaction completes.

For example, imagine a hypothetical DataConsumer class that uses a transaction to try to fetch some network data and process it:

class DataConsumer
{
    let transaction: NetworkTransaction

    init(transaction: NetworkTransaction)
    {
        self.transaction = transaction
    }

    func fetchData()
    {
        transaction.execute() { [weak self] result in
            guard let strongSelf = self else { return }

            switch result {
            case .Succeeded(let data):
                strongSelf.processData(data)

            case .Failed(let err):
                strongSelf.handleError(err)
            }
        }
    }

    func processData(data: NSData)
    {
        // process the data
    }

    func handleError(error: ErrorType)
    {
        // handle the error
    }
}
You'll notice the [weak self]/strongSelf dance in the fetchData() function. This is a common pattern with asynchronously-executed closures, and it signals the possibility that a closure might outlive its usefulness.

Because the NetworkTransaction may complete at any time, it is possible that the closure will execute after the DataConsumer that initiated the transaction has been deallocated. Perhaps the user has navigated elsewhere in the application and whatever data was to be fetched by DataConsumer is no longer needed.

In this case, after a DataConsumer instance goes away, we don't really want the closure doing anything. So, we capture self weakly to ensure that the closure doesn't hold a reference to the owning DataConsumer. That prevents a reference cycle and ensures that DataConsumer can be deallocated when no longer in use.

When it comes time to execute the closure, the guard statement effectively asks the question, "Is self still alive?" If the answer is no, the guard forces a return and the rest of the closure does not execute.

If self is still alive, then the weakly-captured self will be non-nil and it will be converted into a strong reference held by strongSelf for the duration of the closure's execution.

When the closure is done executing, strongSelf goes away, once again making the DataConsumer eligible for deallocation when no other references are held.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#the-problem>The Problem

The [weak self]/strongSelf dance requires common boilerplate wherever it is used, and the fact that a self-like variable with an arbitrary name adds noise within the closure. The more strongSelf is needed within the closure, the more noise there is.

Further, using a consistent name like strongSelf is by convention only; it can't be enforced by the compiler, so searching your codebase for a given keyword won't be exhaustive if team members use different names.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#proposed-solution>Proposed Solution

The proposed solution adds a new capture type by repurposing the guard keyword for another use, which would look like:

transaction.execute() { [guard self] result in
    switch result {
    case .Succeeded(let data):
        self.processData(data)

    case .Failed(let err):
        self.handleError(err)
    }
}
Here, the [guard self] capture list serves as a signal that the compiler should handle the weak/strong dance itself. When encountering [guard self], the compiler should emit code that does the following:

Captures self in a weak reference on behalf of the closure
Whenever the closure is about to be executed, the weak reference is checked to see if self is still alive
If self is not alive, the closure returns immediately and nothing within the braces is executed
If self is alive, it is upgraded to a strong reference for the lifetime of the closure's execution. Within the closure, self is non-optional, unlike how it would be with a [weak self] capture. When the closure is done executing, the strong reference will be cleared and only the weak reference will be held on behalf of the closure.
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#closures-with-return-values>Closures with Return Values

For closures with a Void return type, the notation is simple, because no explicit value is expected as a result of executing the closure.

Closures with a return value will require an else clause:

let pollHappiness: () -> Bool = { [guard self else false] in
    return self.isHealthy && !self.isHungry && !self.isFearful && self.hasLove
}
Here, the else clause provides a value to return in cases where self has gone away and the guard fails.

In this example, if you call pollHappiness() after self has been deallocated, false will always be returned.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#capturing-other-references>Capturing Other References

This notation is not limited to handling self. These capture lists are valid, too:

let capturingTwo = { [guard self, button] in
    // weakly capture self and button
    // but execute the closure with strong references
    // if and only if self and button still exist
    // when the closure is about to execute
}

let captureWithReturn: () -> UIView = {
    [guard button, window = button!.window else nil] in ...

    // window is captured and stored weakly at the time the
    // closure declaration is encountered during execution;
    // button is guaranteed to be non-nil at that time, but
    // it may go away later, so we guard on button too
}
<https://gist.github.com/emaloney/d34ac9b134ece7c60440#caveats>Caveats

This notation is not intended to be a full-fledged replacement for guard statements within the closure. We are only using guard here as a way to declare a specific memory-management behavior for references.

As a result, we are not attempting to support a where clause or boolean expressions within this notation.

Rather, we're simply adding a new capture behavior and providing a means to specify an early exit if the behavior couldn't be fulfilled because one or more of the required objects was deallocated before the closure was executed.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#impact-on-existing-code>Impact on Existing Code

None, since this does not affect any existing constructs. Implementation of this proposal will not result in any code breakage.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternatives-considered>Alternatives Considered

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#status-quo>Status Quo

The primary alternative is to do nothing, requiring developers to add boilerplate guard code and handle upgrading the weak-to-strong references manually.

As stated above, this leads to needless boilerplate that can easily be factored out by the compiler. Also, the use of a self-like variable with an arbitrary name makes it more difficult to exhaustively find such uses in large projects. With this proposal, searching for the text "[guard" is all that's necessary to find all instances of this memory management technique.

Finally, the need to declare and use alternate names to capture values that already have existing names adds visual clutter to code and serves to obscure the code's original intent, making it harder to reason about.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#alternate-notations>Alternate Notations

The notation proposed above was chosen for concision. Two variations were considered, but rejected, because they add words to the statements without adding meaning.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-weak-after-guard>Requiring weak after guard

This alternate notation used a weak keyword after the guard keyword in the capture list, eg. [guard weak self] instead of [guard self].

The use of the weak keyword did not modify behavior in any way, so it was removed from the proposal to be less verbose.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-return-after-else>Requiring return after else

A longer notation requiring the return keyword for closures specifying return values was considered, but rejected, for similar reasons.

Such a notation would make [guard self else nil] look like [guard self else return nil].

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#requiring-both>Requiring both

If both keywords above were required, the verbiage would become quite unwieldy in the closure's capture list, which often appears on the same line after the opening parenthesis, which itself is often indented:

let toggleHappyButton: () -> Bool? = { [guard weak self, button else return nil] in
    let state = button.highlighted
    button.highlighted = self.pollHappiness()
    return state
}
That seems longer than necessary.

For the reasons above, the two keywords above were not included in this proposal.

<https://gist.github.com/emaloney/d34ac9b134ece7c60440#citations>Citations

Variations on this proposal were discussed earlier in the swift-evolution threads "Wanted: syntactic sugar for [weak self] callbacks <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/008713.html>" and "Allowing guard let self = self else { … } for weakly captured self in a closure. <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160201/009023.html>".
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kurt Werle) #6

On reflection, I have to say that I like this solution best. It is very
similar in flavor to

maybeNilObject?.someMethod()

I might even argue that it should only be used for Void return types, just
like that method call becomes essentially void if the object is nil.

Kurt

···

On Sun, Feb 7, 2016 at 11:05 AM, Kenny Leung via swift-evolution < swift-evolution@swift.org> wrote:

I propose

doit { [weak self, other]? in
    doing some stuff...
}

If any of the capture list evaluates to nil, the block is not executed. If
the block signature has an optional return value, nil is returned. Anything
else, you’re on your own.

--
kurt@CircleW.org
http://www.CircleW.org/kurt/