[Pitch] Introduction of `weak/unowned` closures


(Adrian Zubarev) #1

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

···

--
Adrian Zubarev
Sent with Airmail


Weak closure reference
Pitch: Weak method storage modifiers (aka weak references)
(Matthew Johnson) #2

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

···

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

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


(Gor Gyolchanyan) #3

The benefit that I can see here is the ability to guarantee memory safety on API level, by way of specifying weak closure members. Previously, there way no conceivable way of knowing that because a closure can essentially capture whatever it wants (even the very object it's stored in), so this would be a deterministic way of resolving *all* circular references caused by closures.

The downside is that it would require some heavy-duty closure capture analysis on the compiler's part, so I'd expect it to be deferred to Swift 5 or something.
However, this does play really nicely with the ownership concept that Swift is going for.

I say, let's think this one through very thoroughly and write a very very detailed proposal (including details on how would the core team get around implementing this) and see what it looks like before submitting it.

···

On Jun 10, 2017, at 8:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

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


(Adrian Zubarev) #4

Hi Matthew,

Thank you for the hint. Indeed, at first glance your proposal is very similar to my thinking. I’ll try to dive deeper into your proposal and find out more about the discussion behind it when I get some more time for that.

We could collaborate on a revision for Swift 5 if you would like to, but we wouldn’t need to rush now, because there is plenty of time for that.

Personally I would love to avoid some kind of a prefix like ? if possible.

Brainstorming:

let c1 = weak someObject.method convert it to a weak closure.

let c2: weak (CLOSURETYPE)? = someObject.method Let the compiler do the job for us.

The latter would be great for APIs to avoid the prefix.

.subscribe(onNext: self.bar) weak is inferred and can be omitted

···

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:09:28, Matthew Johnson (matthew@anandabits.com) schrieb:

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

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


(old donkey) #5

Agree with Gor, this can work nicely with ownership concept.Though it will require a lot of compiler analysis, I think as Swift’s goal is to be a safer language, still worth it.

I like this idea. But yes, we need a more detail proposal, this need a lot of work.

···

On 2017年6月10日 -0700 AM10:49, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org>, wrote:

The benefit that I can see here is the ability to guarantee memory safety on API level, by way of specifying weak closure members. Previously, there way no conceivable way of knowing that because a closure can essentially capture whatever it wants (even the very object it's stored in), so this would be a deterministic way of resolving *all* circular references caused by closures.

The downside is that it would require some heavy-duty closure capture analysis on the compiler's part, so I'd expect it to be deferred to Swift 5 or something.
However, this does play really nicely with the ownership concept that Swift is going for.

I say, let's think this one through very thoroughly and write a very very detailed proposal (including details on how would the core team get around implementing this) and see what it looks like before submitting it.

> On Jun 10, 2017, at 8:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:
>
> Hello Evolution,
> I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like
> Optional<() -> Void> or simply (() -> Void)?
> is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure
> { [weak self] in self.foo() }
> to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.
> I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.
> Here is a simple code snippet using RxSwift, which will recreate my issue:
>
> import RxSwift
>
> let test = PublishSubject<Void>()
>
> class A {
>
> let disposeBag = DisposeBag()
>
> func foo() {
> test.asObservable()
> .subscribe(onNext: self.bar) // The issue is here
> .disposed(by: self.disposeBag)
> }
>
> func bar() { print("works") }
> }
>
> let a = A()
> a.foo()
>
> test.onNext(()) // Testing if it works
> test.onCompleted() // Some RxSwift stuff
>
> In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:
> .subscribe(onNext: { [unowned self] in self.bar() })
> (It’s safe to make it unowned because the dispose bag is a member of self.)
> What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:
> let closure1: weak (() -> Void)? = { self.doWhatever() }
> let closure2: weak (() -> Void)? = self.doWhatever
> If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.
> As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.
> func subscribe(onNext: weak ((Swift.E) -> Void)?)
> If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.
> I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.
> If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.
>
>
>
> --
> Adrian Zubarev
> Sent with Airmail
>
> _______________________________________________
> swift-evolution mailing list
> 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


(Paul Cantrell) #6

Being able to specify things about closure capturing at the API level could do wonders for Siesta — though its problems may be beyond the scope of what’s proposed here (or what’s workable at all).

Siesta exposes this API call:

    someResource.addObserver(owner: self) { // (1)
        [weak self] resource, event in
        …
        self.bar()
        …
    }

…but it’s easy to forget that [weak self], and because of Siesta’s observer ownership rules:

    http://bustoutsolutions.github.io/siesta/guide/memory/

…omitting it creates a retain cycle. This same problem also makes this otherwise lovely pattern untenable:

    someResource.addObserver(owner: self, closure: self.fooUpdated) // (2)

To solve this problem, addObserver would need to be able to specify that the object passed as the owner should be weakly captured by the closure. It’s just that one specific •owner• object; everything else should be captured as usual. So it’s not a property of the closure itself, either in the type system or as an argument attribute; it’s a relationship between the closure and other args. Ouch!

At a glance, it looks like Matthew’s guarded closure proposal might solve this in practice for (2) but not for (1)?

In the absence of cycle detection, some way around this pitfall sure would be nice.

Cheers, P

···

On Jun 10, 2017, at 12:49 PM, Gor Gyolchanyan via swift-evolution <swift-evolution@swift.org> wrote:

The benefit that I can see here is the ability to guarantee memory safety on API level, by way of specifying weak closure members. Previously, there way no conceivable way of knowing that because a closure can essentially capture whatever it wants (even the very object it's stored in), so this would be a deterministic way of resolving *all* circular references caused by closures.

The downside is that it would require some heavy-duty closure capture analysis on the compiler's part, so I'd expect it to be deferred to Swift 5 or something.
However, this does play really nicely with the ownership concept that Swift is going for.

I say, let's think this one through very thoroughly and write a very very detailed proposal (including details on how would the core team get around implementing this) and see what it looks like before submitting it.

On Jun 10, 2017, at 8:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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


(Gor Gyolchanyan) #7

I don't think it's a good idea to make the `weak`-ness of the closure part of the type system.
The current approach to `weak/unowned` is that it's a storage class that applies to the variable, not the value.
This makes sense because you don't *use* a weak reference, you only *store* it weakly.
I think this behavior has to stay the same with the addition of closures (being the other reference type along classes) would be eligible for storing by `weak/unowned` reference.

···

On Jun 10, 2017, at 10:39 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Thank you for the hint. Indeed, at first glance your proposal is very similar to my thinking. I’ll try to dive deeper into your proposal and find out more about the discussion behind it when I get some more time for that.

We could collaborate on a revision for Swift 5 if you would like to, but we wouldn’t need to rush now, because there is plenty of time for that.

Personally I would love to avoid some kind of a prefix like ? if possible.

Brainstorming:

let c1 = weak someObject.method convert it to a weak closure.

let c2: weak (CLOSURETYPE)? = someObject.method Let the compiler do the job for us.

The latter would be great for APIs to avoid the prefix.

.subscribe(onNext: self.bar) weak is inferred and can be omitted

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:09:28, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>) schrieb:

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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


(Greg Parker) #8

One way to allow the closure's caller to control the memory management of a value in the closure is to make that value a parameter of the closure rather than a capture.

In this example addObserver() could store the owner in a weak variable and pass it to the closure when called.

    someResource.addObserver(owner: self) {
        owner, resource, event in
        …
        owner.bar()
        …
    }

···

On Jun 10, 2017, at 2:42 PM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

Being able to specify things about closure capturing at the API level could do wonders for Siesta — though its problems may be beyond the scope of what’s proposed here (or what’s workable at all).

Siesta exposes this API call:

    someResource.addObserver(owner: self) { // (1)
        [weak self] resource, event in
        …
        self.bar()
        …
    }

…but it’s easy to forget that [weak self], and because of Siesta’s observer ownership rules:

    http://bustoutsolutions.github.io/siesta/guide/memory/

…omitting it creates a retain cycle. This same problem also makes this otherwise lovely pattern untenable:

    someResource.addObserver(owner: self, closure: self.fooUpdated) // (2)

To solve this problem, addObserver would need to be able to specify that the object passed as the owner should be weakly captured by the closure. It’s just that one specific •owner• object; everything else should be captured as usual. So it’s not a property of the closure itself, either in the type system or as an argument attribute; it’s a relationship between the closure and other args. Ouch!

--
Greg Parker gparker@apple.com <mailto:gparker@apple.com> Runtime Wrangler


(Adrian Zubarev) #9

Well I simply reused the existing keywords as part of the closure type to demonstrate the idea here as easy as possible.

About the _true_ weak/unowned closures. First I asked John McCall on twitter about this whole idea. Here is his reply about the _true_ weak closures:

You mean a weak reference to a closure? No, I don’t think we’d ever want to assign closure values enough identity to give that meaning.
I get it, that it might be not quite obvious or even confusing why weak/unowned can be applied on a type and on a variable. However weak is always an Optional or an IUO where unowned is non-optional.

Just my personal thoughts: Could we potentially align weak/unowned with inout and make it type decoration? (This would be a breaking change.)

···

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:44:41, Gor Gyolchanyan (gor@gyolchanyan.com) schrieb:

I don't think it's a good idea to make the `weak`-ness of the closure part of the type system.
The current approach to `weak/unowned` is that it's a storage class that applies to the variable, not the value.
This makes sense because you don't *use* a weak reference, you only *store* it weakly.
I think this behavior has to stay the same with the addition of closures (being the other reference type along classes) would be eligible for storing by `weak/unowned` reference.

On Jun 10, 2017, at 10:39 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Thank you for the hint. Indeed, at first glance your proposal is very similar to my thinking. I’ll try to dive deeper into your proposal and find out more about the discussion behind it when I get some more time for that.

We could collaborate on a revision for Swift 5 if you would like to, but we wouldn’t need to rush now, because there is plenty of time for that.

Personally I would love to avoid some kind of a prefix like ? if possible.

Brainstorming:

let c1 = weak someObject.method convert it to a weak closure.

let c2: weak (CLOSURETYPE)? = someObject.method Let the compiler do the job for us.

The latter would be great for APIs to avoid the prefix.

.subscribe(onNext: self.bar) weak is inferred and can be omitted

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:09:28, Matthew Johnson (matthew@anandabits.com) schrieb:

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
swift-evolution mailing list
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


(Adrian Zubarev) #10

Just a few more replies from John McCall:

You want to invalidate the closure value based on whether any of its captures have been invalidated.

It’s an interesting idea, but it’s quite complex because an invalidatable closure is definitely a different sort of thing from a normal one.

So like you say, it would need to be tracked in the type, which is not the language model we otherwise use for closures. Er, for weak/unowned, I mean.

The behavior for unowned is not too different from the current behavior, so it’s really mostly about weak.

I guess the captured reference becomes strong immediately, protecting against it going away during the closure.

But unowned is conventionally used in cases where that isn’t going to happen anyway.

The semantics of testing a “weak closure” are not obvious. Always must be wrapped in optional, unwrapping it gives a value of non-weak type?

Totally different from normal optional behavior on values, but I guess it works.

···

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:58:21, Adrian Zubarev (adrian.zubarev@devandartist.com) schrieb:

Well I simply reused the existing keywords as part of the closure type to demonstrate the idea here as easy as possible.

About the _true_ weak/unowned closures. First I asked John McCall on twitter about this whole idea. Here is his reply about the _true_ weak closures:

You mean a weak reference to a closure? No, I don’t think we’d ever want to assign closure values enough identity to give that meaning.
I get it, that it might be not quite obvious or even confusing why weak/unowned can be applied on a type and on a variable. However weak is always an Optional or an IUO where unowned is non-optional.

Just my personal thoughts: Could we potentially align weak/unowned with inout and make it type decoration? (This would be a breaking change.)

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:44:41, Gor Gyolchanyan (gor@gyolchanyan.com) schrieb:

I don't think it's a good idea to make the `weak`-ness of the closure part of the type system.
The current approach to `weak/unowned` is that it's a storage class that applies to the variable, not the value.
This makes sense because you don't *use* a weak reference, you only *store* it weakly.
I think this behavior has to stay the same with the addition of closures (being the other reference type along classes) would be eligible for storing by `weak/unowned` reference.

On Jun 10, 2017, at 10:39 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hi Matthew,

Thank you for the hint. Indeed, at first glance your proposal is very similar to my thinking. I’ll try to dive deeper into your proposal and find out more about the discussion behind it when I get some more time for that.

We could collaborate on a revision for Swift 5 if you would like to, but we wouldn’t need to rush now, because there is plenty of time for that.

Personally I would love to avoid some kind of a prefix like ? if possible.

Brainstorming:

let c1 = weak someObject.method convert it to a weak closure.

let c2: weak (CLOSURETYPE)? = someObject.method Let the compiler do the job for us.

The latter would be great for APIs to avoid the prefix.

.subscribe(onNext: self.bar) weak is inferred and can be omitted

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:09:28, Matthew Johnson (matthew@anandabits.com) schrieb:

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
swift-evolution mailing list
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


(Gor Gyolchanyan) #11

If weak closures ever become a thing, I do think that the implicit strong promotion of the closure's captures should never happen and instead, the closure should be manually optional-unwrapped before it's called, so that the existing safety guarantees of weak object references would persist.

···

On Jun 10, 2017, at 11:04 PM, Adrian Zubarev <adrian.zubarev@devandartist.com> wrote:

Just a few more replies from John McCall:

You want to invalidate the closure value based on whether any of its captures have been invalidated.

It’s an interesting idea, but it’s quite complex because an invalidatable closure is definitely a different sort of thing from a normal one.

So like you say, it would need to be tracked in the type, which is not the language model we otherwise use for closures. Er, for weak/unowned, I mean.

The behavior for unowned is not too different from the current behavior, so it’s really mostly about weak.

I guess the captured reference becomes strong immediately, protecting against it going away during the closure.

But unowned is conventionally used in cases where that isn’t going to happen anyway.

The semantics of testing a “weak closure” are not obvious. Always must be wrapped in optional, unwrapping it gives a value of non-weak type?

Totally different from normal optional behavior on values, but I guess it works.

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:58:21, Adrian Zubarev (adrian.zubarev@devandartist.com <mailto:adrian.zubarev@devandartist.com>) schrieb:

Well I simply reused the existing keywords as part of the closure type to demonstrate the idea here as easy as possible.

About the _true_ weak/unowned closures. First I asked John McCall on twitter about this whole idea. Here is his reply about the _true_ weak closures:

You mean a weak reference to a closure? No, I don’t think we’d ever want to assign closure values enough identity to give that meaning.
I get it, that it might be not quite obvious or even confusing why weak/unowned can be applied on a type and on a variable. However weak is always an Optional or an IUO where unowned is non-optional.

Just my personal thoughts: Could we potentially align weak/unowned with inout and make it type decoration? (This would be a breaking change.)

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:44:41, Gor Gyolchanyan (gor@gyolchanyan.com <mailto:gor@gyolchanyan.com>) schrieb:

I don't think it's a good idea to make the `weak`-ness of the closure part of the type system.
The current approach to `weak/unowned` is that it's a storage class that applies to the variable, not the value.
This makes sense because you don't *use* a weak reference, you only *store* it weakly.
I think this behavior has to stay the same with the addition of closures (being the other reference type along classes) would be eligible for storing by `weak/unowned` reference.

On Jun 10, 2017, at 10:39 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi Matthew,

Thank you for the hint. Indeed, at first glance your proposal is very similar to my thinking. I’ll try to dive deeper into your proposal and find out more about the discussion behind it when I get some more time for that.

We could collaborate on a revision for Swift 5 if you would like to, but we wouldn’t need to rush now, because there is plenty of time for that.

Personally I would love to avoid some kind of a prefix like ? if possible.

Brainstorming:

let c1 = weak someObject.method convert it to a weak closure.

let c2: weak (CLOSURETYPE)? = someObject.method Let the compiler do the job for us.

The latter would be great for APIs to avoid the prefix.

.subscribe(onNext: self.bar) weak is inferred and can be omitted

--
Adrian Zubarev
Sent with Airmail

Am 10. Juni 2017 um 21:09:28, Matthew Johnson (matthew@anandabits.com <mailto:matthew@anandabits.com>) schrieb:

Hi Adrian, this is pretty similar to the Guarded Closures proposal I drafted in February. This proposal needs a revision incorporating discussion feedback and some new ideas. If you’re interested, here’s a link to the original discussion thread: https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20170213/032478.html.

On Jun 10, 2017, at 12:29 PM, Adrian Zubarev via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hello Evolution,

I’d like to pitch a new idea and see where it would go. Recently I tapped into a small trap and just now realized that even that non-escaping should have been the default for closures (SE–0103) there is an exception for that. Apparently generics don’t follow that rule and a closure like

Optional<() -> Void> or simply (() -> Void)?

is still escaping by default. But that was the half of the story yet. As we all know and “love” reference lists inside closures, methods don’t have any and we have to wrap method calls into a weak referenced closure

{ [weak self] in self.foo() }

to avoid strong reference cycles. Maybe you already guess it, I accidentally didn’t and tapped into the land of strong reference cycles yet again on my journey.

I’d like to pitch a new way, more like a new type behavior, for closures on how they could be used differently in order to avoid strong reference cycles but also providing the ability to use methods without any need to wrap them.

Here is a simple code snippet using RxSwift, which will recreate my issue:

import RxSwift

let test = PublishSubject<Void>()

class A {

    let disposeBag = DisposeBag()

    func foo() {
        test.asObservable()
            .subscribe(onNext: self.bar) // The issue is here
            .disposed(by: self.disposeBag)
    }

    func bar() { print("works") }
}

let a = A()
a.foo()

test.onNext(()) // Testing if it works
test.onCompleted() // Some RxSwift stuff
In this case by passing directly the method self.bar we’re capturing self, which in this situation isn’t our intention at all. To avoid this issue we can simply wrap the method call into closure:

.subscribe(onNext: { [unowned self] in self.bar() })

(It’s safe to make it unowned because the dispose bag is a member of self.)

What if we had the ability for weak or unowned closures? By that I don’t mean weak/unowned references to the closures themselves, because they are also reference types, but an invalidation behavior for the whole closure based on the _captured_ references. For instance:

let closure1: weak (() -> Void)? = { self.doWhatever() }

let closure2: weak (() -> Void)? = self.doWhatever

If one would now try to call the closure, first it will check if all the captured objects are still available or not, if not the whole closure in this case will simply become nil and won’t execute. In case of unowned closures it will trap. Furthermore it will support the general meaning of weak/unowned and will not increase the reference counter for *captured objects*.

As you have already noticed, in this case the convention is slightly different because we must carry the behavior directly with the type.

func subscribe(onNext: weak ((Swift.E) -> Void)?)

If the way of my thinking is correct this idea _could maybe_ fade out the very common [weak self] in guard let strongSelf = self … pattern.

I personally cannot tell all the technical difficulties this idea might have, but that’s what the evolution list is for, to collaboratively flesh out the ideas if they are worth it.

If something like this could be possible it’s probably worth noting that we might also be able to introduce something like @autoclosure(weak/unowned) to Swift for consistency.

--
Adrian Zubarev
Sent with Airmail

_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution