Proposal: Closures capture weak by default


(Andrew Bennett) #1

From https://swift.org/about/: "The most obvious way to write code should

also behave in a safe manner."

To this end I think that closures should capture references types weakly by
default. Pretty much the only way I know of to (easily) create memory
issues with pure swift is to capture a strong reference in a closure.

I think with consideration when designing asynchronous APIs this could be
quite painless.

Cases weak may be excluded:
* If the closure is @noescape
* If the object's lifetime is provably limited to the block
* If it's a value type

I think the upsides by far outweigh the downsides.

Upside:
* no more surprises
* safer code

Downsides:
* You may sometimes have to use optional chaining or similar to resolve a
weak reference.
* Beginners need to understand optionals, but they're likely to do so
before learning blocks.
* There's probably a few edge cases I haven't explored, and a few more
here:

class Test {
   func doSomething(v: Int) { ... }
   func async(callback: Int->Void) {
      doWork { value in
           callback?(value)
      }
   }
}

self.test = Test()
self.test.async(test.doSomething) // what is the lifetime
of test.doSomething?


(Joe Groff) #2

Capturing weak by default is not safe. ARC optimization will shrinkwrap the lifetimes of strong references to their last strong use, so capturing a local variable that references a fresh object would cause the object to immediately go away:

let x = NSObject()
dispatch_sync {[weak x] in x!.foo() } // x can already be gone by this point

There's no one right answer here.

-Joe

···

On Dec 8, 2015, at 3:15 AM, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

From https://swift.org/about/: "The most obvious way to write code should also behave in a safe manner."

To this end I think that closures should capture references types weakly by default. Pretty much the only way I know of to (easily) create memory issues with pure swift is to capture a strong reference in a closure.

I think with consideration when designing asynchronous APIs this could be quite painless.

Cases weak may be excluded:
* If the closure is @noescape
* If the object's lifetime is provably limited to the block
* If it's a value type

I think the upsides by far outweigh the downsides.

Upside:
* no more surprises
* safer code

Downsides:
* You may sometimes have to use optional chaining or similar to resolve a weak reference.
* Beginners need to understand optionals, but they're likely to do so before learning blocks.
* There's probably a few edge cases I haven't explored, and a few more here:

class Test {
   func doSomething(v: Int) { ... }
   func async(callback: Int->Void) {
      doWork { value in
           callback?(value)
      }
   }
}

self.test = Test()
self.test.async(test.doSomething) // what is the lifetime of test.doSomething?

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


(Joe Groff) #3

One reason cycles are so prevalent with closures is that it's easy to capture too much, since referencing 'self.foo' always captures the entirety of 'self'. If the properties of 'self' you need are immutable, or you don't need to see their mutations, you can reduce the risk of cycles by capturing those properties explicitly, turning this:

self.setCallback { doStuffWith(self.zim, self.zang) }

into:

self.setCallback {[zim, zang] in doStuffWith(zim, zang) }

For 'let' properties of classes, it'd be reasonable to propose having closures capture the *property* directly by default in this way instead of capturing 'self' (and possibly allowing referencing them without 'self.', since 'self' wouldn't be involved in any cycle formed this way).

-Joe

···

On Dec 8, 2015, at 7:49 AM, Joe Groff <jgroff@apple.com> wrote:

Capturing weak by default is not safe. ARC optimization will shrinkwrap the lifetimes of strong references to their last strong use, so capturing a local variable that references a fresh object would cause the object to immediately go away:

let x = NSObject()
dispatch_sync {[weak x] in x!.foo() } // x can already be gone by this point

There's no one right answer here.


(Paul Cantrell) #4

For 'let' properties of classes, it'd be reasonable to propose having closures capture the *property* directly by default in this way instead of capturing ‘self'

That would be fantastic.

I’ve also wondered whether there’s a tidy way for a closure to be tied to the lifecycle of self, so that when self goes away, the closure goes away too. That’s often the desired behavior. On a casual thinking through, though, it seems like a can of worms — and the “guard let self = self else { return }” proposal on another thread gives most of the same benefit.

P


(Lily Ballard) #5

Interesting idea, but I'm concerned that this would be surprising and
open up a different class of bug, where properties of self
unexpectedly outlive self even though they were never referenced
without going through self. This can be a problem if self has a
deinit that does relevant work, or if the values of the properties
themselves depend on self somehow. As a trivial example, a property
of self might contain an unowned reference back to self, because it
knows it will never outlive self, at least until this rule is
introduced and causes a dispatch_async() to access the unowned
reference after self has deinited.

Personally, I'm happy with just using an explicit capture list for my
properties if I don't want to capture self. Using this along with the
practice of always omitting the `self.` whenever possible means the
compiler yells at me if I accidentally capture self in an escaping
closure (e.g. when I try and use a method/property without the `self.`),
forcing me to make a decision there about whether I want to capture self
or add a capture list for the property.

Another alternative is to require an explicit capture list to capture
self in an escaping closure, so you have to say `[self]` or `[weak
self]`. This way you're forced to make a decision always. Potential
downsides I can think of:

1. It's verbose. Application code often has a lot of escaping closures,
   and most of them probably don't have capture lists, so that's a lot
   of capture lists to add.
2. It's still possible to accidentally capture self without realizing it
   by use of a local nested function that is itself captured. Of course,
   this is the same scenario that lets you capture self without even
   writing `self.` since local nested functions allow you to omit the
   `self.`, so it's already a known issue.
3. How does this interact with captures of self from enclosing closures?
   Right now, if I have `{ [weak self] in foo({ /* self is still weak
   here */ }) }`; does the inner closure need a capture list or not? How
   about if the outer capture is strong? `{ [self] in foo({ /* self is
   strong here */ }) }`? Of course, this rule would fix the current
   hazard of weakly capturing self at the wrong level, e.g. `{ foo({
   [weak self] in ... }) }` where self is strongly captured in the outer
   closure, which seems to surprise people. Although now that I think of
   it, I'll probably submit a proposal to change that, so the outer
   closure captures self weakly if it's only capturing it at all because
   of a weak capture in a nested closure.

-Kevin Ballard

···

On Tue, Dec 8, 2015, at 07:55 AM, Joe Groff via swift-evolution wrote:

One reason cycles are so prevalent with closures is that it's easy to capture too much, since referencing 'self.foo' always captures the entirety of 'self'. If the properties of 'self' you need are immutable, or you don't need to see their mutations, you can reduce the risk of cycles by capturing those properties explicitly, turning this:

self.setCallback { doStuffWith(self.zim, self.zang) }

into:

self.setCallback {[zim, zang] in doStuffWith(zim, zang) }

For 'let' properties of classes, it'd be reasonable to propose having
closures capture the *property* directly by default in this way
instead of capturing 'self' (and possibly allowing referencing them
without 'self.', since 'self' wouldn't be involved in any cycle formed
this way).


(ilya) #6

I don't think this would satisfy the principle of "working as expected",
e.g.:

class Computer {
    func compute() -> Int { ... }
}

func test() {

    let c = Computer()

    // Expected: the closure executes in the background
    dispatch_async(...) {

        let computed = c.compute()
        print(computed)

        let computed2 = c.compute()
        print(computed2)
    }

}

Actual result if the proposal passes: the closure will not compile as c
will be optional.

Moreover, you'll have some beginners who will fix it with

    dispatch_async(...) {

        if let computed = c?.compute() {
            print(computed)
        }

        if let computed2 = c?.compute() {
            print(computed2)
        }
    }

which has entirely different logic, as now any of those situations is
possible:
(1) both computed and computed2 are printed
(2) none of those is printed
(3) only computed is printed

···

On Tue, Dec 8, 2015 at 14:15 Andrew Bennett via swift-evolution < swift-evolution@swift.org> wrote:

From https://swift.org/about/: "The most obvious way to write code should
also behave in a safe manner."

To this end I think that closures should capture references types weakly
by default. Pretty much the only way I know of to (easily) create memory
issues with pure swift is to capture a strong reference in a closure.

I think with consideration when designing asynchronous APIs this could be
quite painless.

Cases weak may be excluded:
* If the closure is @noescape
* If the object's lifetime is provably limited to the block
* If it's a value type

I think the upsides by far outweigh the downsides.

Upside:
* no more surprises
* safer code

Downsides:
* You may sometimes have to use optional chaining or similar to resolve a
weak reference.
* Beginners need to understand optionals, but they're likely to do so
before learning blocks.
* There's probably a few edge cases I haven't explored, and a few more
here:

class Test {
   func doSomething(v: Int) { ... }
   func async(callback: Int->Void) {
      doWork { value in
           callback?(value)
      }
   }
}

self.test = Test()
self.test.async(test.doSomething) // what is the lifetime
of test.doSomething?

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


(Javier Soto) #7

I would like to also note that referencing an object (whether self or
otherwise) does not necessarily create a permanent retain cycle. Only
long-lived closures will cause these issues. In many other cases, retaining
objects inside a closure is actually what you want, to ensure they're still
alive by the time the closure is called.
In general, no matter what the default is, my hunch is that this is one of
those "hard problems" (essential complexity:
https://en.m.wikipedia.org/wiki/No_Silver_Bullet), and one must think about
the lifetime semantics of the constructions in their code and the APIs
they're using to understand the implications of using weak or strong
captures, and whether they will cause retain cycles or premature
deallocations.

···

On Tue, Dec 8, 2015 at 4:08 AM ilya via swift-evolution < swift-evolution@swift.org> wrote:

I don't think this would satisfy the principle of "working as expected",
e.g.:

class Computer {
    func compute() -> Int { ... }
}

func test() {

    let c = Computer()

    // Expected: the closure executes in the background
    dispatch_async(...) {

        let computed = c.compute()
        print(computed)

        let computed2 = c.compute()
        print(computed2)
    }

}

Actual result if the proposal passes: the closure will not compile as c
will be optional.

Moreover, you'll have some beginners who will fix it with

    dispatch_async(...) {

        if let computed = c?.compute() {
            print(computed)
        }

        if let computed2 = c?.compute() {
            print(computed2)
        }
    }

which has entirely different logic, as now any of those situations is
possible:
(1) both computed and computed2 are printed
(2) none of those is printed
(3) only computed is printed

On Tue, Dec 8, 2015 at 14:15 Andrew Bennett via swift-evolution < > swift-evolution@swift.org> wrote:

From https://swift.org/about/: "The most obvious way to write code
should also behave in a safe manner."

To this end I think that closures should capture references types weakly
by default. Pretty much the only way I know of to (easily) create memory
issues with pure swift is to capture a strong reference in a closure.

I think with consideration when designing asynchronous APIs this could be
quite painless.

Cases weak may be excluded:
* If the closure is @noescape
* If the object's lifetime is provably limited to the block
* If it's a value type

I think the upsides by far outweigh the downsides.

Upside:
* no more surprises
* safer code

Downsides:
* You may sometimes have to use optional chaining or similar to resolve
a weak reference.
* Beginners need to understand optionals, but they're likely to do so
before learning blocks.
* There's probably a few edge cases I haven't explored, and a few more
here:

class Test {
   func doSomething(v: Int) { ... }
   func async(callback: Int->Void) {
      doWork { value in
           callback?(value)
      }
   }
}

self.test = Test()
self.test.async(test.doSomething) // what is the lifetime
of test.doSomething?

_______________________________________________
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

--
Javier Soto


(ilya) #8

Joe, a very good idea to allow directly capturing a value bound to an
immutable name without self keyword!

···

On Tue, Dec 8, 2015 at 18:55 Joe Groff via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 8, 2015, at 7:49 AM, Joe Groff <jgroff@apple.com> wrote:

Capturing weak by default is not safe. ARC optimization will shrinkwrap
the lifetimes of strong references to their last strong use, so capturing a
local variable that references a fresh object would cause the object to
immediately go away:

let x = NSObject()
dispatch_sync {[weak x] in x!.foo() } // x can already be gone by this
point

There's no one right answer here.

One reason cycles are so prevalent with closures is that it's easy to
capture too much, since referencing 'self.foo' always captures the entirety
of 'self'. If the properties of 'self' you need are immutable, or you don't
need to see their mutations, you can reduce the risk of cycles by capturing
those properties explicitly, turning this:

self.setCallback { doStuffWith(self.zim, self.zang) }

into:

self.setCallback {[zim, zang] in doStuffWith(zim, zang) }

For 'let' properties of classes, it'd be reasonable to propose having
closures capture the *property* directly by default in this way instead of
capturing 'self' (and possibly allowing referencing them without 'self.',
since 'self' wouldn't be involved in any cycle formed this way).

-Joe

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


#9

I’m really not found at all of having `let` properties captured by closures when used without explicit `self`.

Three reasons:

REASON 1. When `self` is not mandatory, then adding self should have no impact whatsoever.

Why should one add a self that is not mandatory?

1. for symmetry (1):

  self.foo = other.foo
  let delta = self.x - other.x

2. for symmetry (2):
  
  init(a:String, index:Int) {
    self.name = name
    self.position = index+1 // keep 1-based value for implementation reasons
  }

3. add your own reasons to use an explicit self even when not required, and I’m sure you did.

So generally speaking, using self when not mandatory should have no impact on the program at all.

REASON 2. What happens when a property starts its life as `let`, but turns `var` eventually? For example, the property eventually becomes lazy. OK now it’s is illegal to use the property without explicit `self` in closures, and you break a bunch of code. And you need to bump the major version of your package. Just because you turn a `let foo: String` into `lazy var foo: String = …`.

That’s not good at all.

REASON 3. It’s simply not clear. Clever, terse, smart, brilliant, but not clear.

Compare to:

  // Obviously only `foo` is captured, and not self:
  let foo = self.foo
  performClosure { foo }

My two cents.
Gwendal Roué

···

Le 8 déc. 2015 à 18:01, Paul Cantrell via swift-evolution <swift-evolution@swift.org> a écrit :

For 'let' properties of classes, it'd be reasonable to propose having closures capture the *property* directly by default in this way instead of capturing ‘self'

That would be fantastic.

I’ve also wondered whether there’s a tidy way for a closure to be tied to the lifecycle of self, so that when self goes away, the closure goes away too. That’s often the desired behavior. On a casual thinking through, though, it seems like a can of worms — and the “guard let self = self else { return }” proposal on another thread gives most of the same benefit.

P

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


#10

Oh, and the concerned reader will have noticed another use of a non-mandatory self in the last sample code:

  // Obviously only `foo` is captured, and not self:
  let foo = self.foo
  performClosure { foo }

Gwendal Roué

···

Le 8 déc. 2015 à 18:28, Gwendal Roué <gwendal.roue@gmail.com> a écrit :

I’m really not found at all of having `let` properties captured by closures when used without explicit `self`.

Three reasons:

REASON 1. When `self` is not mandatory, then adding self should have no impact whatsoever.

Why should one add a self that is not mandatory?

1. for symmetry (1):

  self.foo = other.foo
  let delta = self.x - other.x

2. for symmetry (2):
  
  init(a:String, index:Int) {
    self.name = name
    self.position = index+1 // keep 1-based value for implementation reasons
  }

3. add your own reasons to use an explicit self even when not required, and I’m sure you did.

So generally speaking, using self when not mandatory should have no impact on the program at all.

REASON 2. What happens when a property starts its life as `let`, but turns `var` eventually? For example, the property eventually becomes lazy. OK now it’s is illegal to use the property without explicit `self` in closures, and you break a bunch of code. And you need to bump the major version of your package. Just because you turn a `let foo: String` into `lazy var foo: String = …`.

That’s not good at all.

REASON 3. It’s simply not clear. Clever, terse, smart, brilliant, but not clear.

Compare to:

  // Obviously only `foo` is captured, and not self:
  let foo = self.foo
  performClosure { foo }

My two cents.
Gwendal Roué

Le 8 déc. 2015 à 18:01, Paul Cantrell via swift-evolution <swift-evolution@swift.org> a écrit :

For 'let' properties of classes, it'd be reasonable to propose having closures capture the *property* directly by default in this way instead of capturing ‘self'

That would be fantastic.

I’ve also wondered whether there’s a tidy way for a closure to be tied to the lifecycle of self, so that when self goes away, the closure goes away too. That’s often the desired behavior. On a casual thinking through, though, it seems like a can of worms — and the “guard let self = self else { return }” proposal on another thread gives most of the same benefit.

P

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


(ilya) #11

As for 1, isn't the compiler allowed to optimize away self.foo call?

More generally. if we have

class X { let const = 4 }

let x = X()
let closure = { print(x.const) }

is the compiler required to actually capture x? Can't it rewrite closure as
{ print(4) }? I feel like memory semantics is/should be an implementation
detail.

Ilya.

···

On Tue, Dec 8, 2015 at 8:28 PM, Gwendal Roué <swift-evolution@swift.org> wrote:

I’m really not found at all of having `let` properties captured by
closures when used without explicit `self`.

Three reasons:

REASON 1. When `self` is not mandatory, then adding self should have no
impact whatsoever.

Why should one add a self that is not mandatory?

1. for symmetry (1):

        self.foo = other.foo
        let delta = self.x - other.x

2. for symmetry (2):

        init(a:String, index:Int) {
                self.name = name
                self.position = index+1 // keep 1-based value for
implementation reasons
        }

3. add your own reasons to use an explicit self even when not required,
and I’m sure you did.

So generally speaking, using self when not mandatory should have no impact
on the program at all.

REASON 2. What happens when a property starts its life as `let`, but turns
`var` eventually? For example, the property eventually becomes lazy. OK now
it’s is illegal to use the property without explicit `self` in closures,
and you break a bunch of code. And you need to bump the major version of
your package. Just because you turn a `let foo: String` into `lazy var foo:
String = …`.

That’s not good at all.

REASON 3. It’s simply not clear. Clever, terse, smart, brilliant, but not
clear.

Compare to:

        // Obviously only `foo` is captured, and not self:
        let foo = self.foo
        performClosure { foo }

My two cents.
Gwendal Roué

> Le 8 déc. 2015 à 18:01, Paul Cantrell via swift-evolution < > swift-evolution@swift.org> a écrit :
>
>> For 'let' properties of classes, it'd be reasonable to propose having
closures capture the *property* directly by default in this way instead of
capturing ‘self'
>
> That would be fantastic.
>
> I’ve also wondered whether there’s a tidy way for a closure to be tied
to the lifecycle of self, so that when self goes away, the closure goes
away too. That’s often the desired behavior. On a casual thinking through,
though, it seems like a can of worms — and the “guard let self = self else
{ return }” proposal on another thread gives most of the same benefit.
>
> P
>
> _______________________________________________
> 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


(Andrew Bennett) #12

Thanks everyone for joining the discussion, I'm glad that other people are
thinking about this too. I should have mentioned up-front, in case it was
unclear, I think this needs more discussion before it's ready as a formal
proposal.

I think most people here seem to agree that blocks capturing objects can be
a problem, and we don't want to have to type a bunch of new things to deal
with it. What we're unsure of is how to deal with it.

The only thing I explicitly didn't agree with was that { self!.foo } is a
problem because of self being weak. I think the problem here is the use of
force-unwrap.

Also, I think that the discussion about when properties are immutable, and
not necessarily being able to configure an immutable structure is a good
one. It is also perhaps off-topic, except where it affects the scope of
impact of this proposal. Do we agree that it's sufficient for this proposal
to say that some properties could be captured as value types, and as the
swift compiler gets smarter those cases will grow?

In Ilya's example:
if let computed = c?.compute() {
    print(computed)
}
I agree that this will change what the code does, but I think a programmer
using dispatch_async will be familiar with if-statements, and so should be
able to determine the implications. I think that in this case the IDE would
suggest two solutions "add ? to c.compute()" and "strongly capture c <a
warning about retain cycles>". I think the important thing would be that
the "default" case is that you don't have memory issues, you can't compile
the code if you haven't acknowledged that with '?' or '[strong c]'.

Kevin, I think you should definitely propose that (weak in outer and inner
closure), it's one of the surprises that led me to this proposal. I also
really like your suggesting of requiring explicit weak/strong on self, I
think this could solve many cases. What I'm not sure of is whether this
should be restricted to self, or the "owners" of the block calling method.
ie.

func doWork(something: Something) {
     something.calculateSomeStuff { (a,b) in
          return self.calculate(a,b)
     }
}

In this case I think that strong/weak should be made explicit on
'something' not on 'self'.

I think that the main points at the moment are:
1. The impact of requiring explicit strong/weak on self/something.
2. How often requiring 'strong' on *non-trivial* captures would happen.
3. If you can mitigate most cases with small changes to APIs.

An example of an api change would be:

let data = 123
worker.doWork { [strong worker] (item: Item) in
    worker.work(data, withItem: item)
}

becomes:

let data = 123
worker.doWork { (worker: Worker, item: Item) in
    worker.work(data, withItem: item)
}

···

On Wed, Dec 9, 2015 at 6:28 AM, Kevin Ballard via swift-evolution < swift-evolution@swift.org> wrote:

On Tue, Dec 8, 2015, at 07:55 AM, Joe Groff via swift-evolution wrote:

One reason cycles are so prevalent with closures is that it's easy to
capture too much, since referencing 'self.foo' always captures the entirety
of 'self'. If the properties of 'self' you need are immutable, or you don't
need to see their mutations, you can reduce the risk of cycles by capturing
those properties explicitly, turning this:

self.setCallback { doStuffWith(self.zim, self.zang) }

into:

self.setCallback {[zim, zang] in doStuffWith(zim, zang) }

For 'let' properties of classes, it'd be reasonable to propose having
closures capture the *property* directly by default in this way instead of
capturing 'self' (and possibly allowing referencing them without 'self.',
since 'self' wouldn't be involved in any cycle formed this way).

Interesting idea, but I'm concerned that this would be surprising and open
up a different class of bug, where properties of self unexpectedly outlive
self even though they were never referenced without going through self.
This can be a problem if self has a deinit that does relevant work, or if
the values of the properties themselves depend on self somehow. As a
trivial example, a property of self might contain an unowned reference back
to self, because it knows it will never outlive self, at least until this
rule is introduced and causes a dispatch_async() to access the unowned
reference after self has deinited.

Personally, I'm happy with just using an explicit capture list for my
properties if I don't want to capture self. Using this along with the
practice of always omitting the `self.` whenever possible means the
compiler yells at me if I accidentally capture self in an escaping closure
(e.g. when I try and use a method/property without the `self.`), forcing me
to make a decision there about whether I want to capture self or add a
capture list for the property.

Another alternative is to require an explicit capture list to capture self
in an escaping closure, so you have to say `[self]` or `[weak self]`. This
way you're forced to make a decision always. Potential downsides I can
think of:

1. It's verbose. Application code often has a lot of escaping closures,
and most of them probably don't have capture lists, so that's a lot of
capture lists to add.
2. It's still possible to accidentally capture self without realizing it
by use of a local nested function that is itself captured. Of course, this
is the same scenario that lets you capture self without even writing
`self.` since local nested functions allow you to omit the `self.`, so it's
already a known issue.
3. How does this interact with captures of self from enclosing closures?
Right now, if I have `{ [weak self] in foo({ /* self is still weak here */
}) }`; does the inner closure need a capture list or not? How about if the
outer capture is strong? `{ [self] in foo({ /* self is strong here */ })
}`? Of course, this rule would fix the current hazard of weakly capturing
self at the wrong level, e.g. `{ foo({ [weak self] in ... }) }` where self
is strongly captured in the outer closure, which seems to surprise people.
Although now that I think of it, I'll probably submit a proposal to change
that, so the outer closure captures self weakly if it's only capturing it
at all because of a weak capture in a nested closure.

-Kevin Ballard

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


(Lily Ballard) #13

I submitted the proposal as
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001134.html
(unfortunately it looks like the plain text version got garbled :/).

Regarding explicit weak/strong, my feeling is it would probably get too
annoying to have to type `[self] in` everywhere. But it may be worth
playing with.

-Kevin Ballard

···

On Tue, Dec 8, 2015, at 02:57 PM, Andrew Bennett wrote:

Kevin, I think you should definitely propose that (weak in outer and
inner closure), it's one of the surprises that led me to this
proposal. I also really like your suggesting of requiring explicit
weak/strong on self, I think this could solve many cases.


(Andrew Bennett) #14

Perhaps this is something where we could have a few compiler options to
handle different preferences.

You could have options like:
* -Wexplicit-owner-capture (for explicit self)
* -Wexplicit-capture (for explicit non-trivial references)

···

On Wed, Dec 9, 2015 at 10:15 AM, Kevin Ballard <kevin@sb.org> wrote:

On Tue, Dec 8, 2015, at 02:57 PM, Andrew Bennett wrote:

Kevin, I think you should definitely propose that (weak in outer and inner
closure), it's one of the surprises that led me to this proposal. I also
really like your suggesting of requiring explicit weak/strong on self, I
think this could solve many cases.

I submitted the proposal as
https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001134.html (unfortunately
it looks like the plain text version got garbled :/).

Regarding explicit weak/strong, my feeling is it would probably get too
annoying to have to type `[self] in` everywhere. But it may be worth
playing with.

-Kevin Ballard


(Jordan Rose) #15

IMO, this has been one of many reasons why Swift doesn't have flags for its warnings yet: we do not want to fracture the language into dialects based on style, and we'd rather not have one person's code spew warnings in another person's build. (Admittedly, the warnings could be controlled by package preferences, but still.)

Jordan

···

On Dec 8, 2015, at 15:26, Andrew Bennett via swift-evolution <swift-evolution@swift.org> wrote:

Perhaps this is something where we could have a few compiler options to handle different preferences.

You could have options like:
* -Wexplicit-owner-capture (for explicit self)
* -Wexplicit-capture (for explicit non-trivial references)

On Wed, Dec 9, 2015 at 10:15 AM, Kevin Ballard <kevin@sb.org <mailto:kevin@sb.org>> wrote:
On Tue, Dec 8, 2015, at 02:57 PM, Andrew Bennett wrote:

Kevin, I think you should definitely propose that (weak in outer and inner closure), it's one of the surprises that led me to this proposal. I also really like your suggesting of requiring explicit weak/strong on self, I think this could solve many cases.

I submitted the proposal as https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001134.html (unfortunately it looks like the plain text version got garbled :/).

Regarding explicit weak/strong, my feeling is it would probably get too annoying to have to type `[self] in` everywhere. But it may be worth playing with.

-Kevin Ballard

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