[swift-evolution] [Proposal] Guarded self in closures

David Hedbor neotron at gmail.com
Wed Feb 22 17:28:48 CST 2017


On Wed, Feb 22, 2017 at 3:09 PM, Matthew Johnson <matthew at anandabits.com>
wrote:

>
> On Feb 22, 2017, at 4:57 PM, David Hedbor <neotron at gmail.com> wrote:
>
>
>
>
> On Wed, Feb 22, 2017 at 2:23 PM, Matthew Johnson <matthew at anandabits.com>
> wrote:
>
>>
>> On Feb 22, 2017, at 4:06 PM, David Hedbor <neotron at gmail.com> wrote:
>>
>> One more thing I'd like to add into the discussion is handling of
>> optional variables. My assumption is that if I have an optional variable in
>> the outer scope, it would remain optional in the inner scope, but the way
>> the draft is worded, it might seem like it would add an implicit guard
>> statement in that situation. i.e:
>>
>>    var opt: Bool?
>>    var closure = ?{
>>       if opt {}
>>    }
>>
>> =>
>>
>>    var opt: Bool?
>>    var closure = {
>>       guard let opt = opt else { return }
>>       if opt {}
>>    }
>>
>> What are your thoughts on this?
>>
>>
>> This is a great question!
>>
>
>> In this example guarded closures make no difference at all because
>> `Bool?` is a value type so it is not captured by reference.
>>
>
> Indeed, using Bool here was a bad choice for my example.
>
>
>>
>> If it was an optional reference type the guard would fire as soon as the
>> value was `nil` which would be immediately if the value was already `nil`
>> when the closure was created.  This is the same behavior you would get
>> today by writing it out manually.
>>
>
>
> I.e unless otherwise overridden with a [weak opt] or similar statement,
> this ?{} syntax would also enforce non-nil status of any passed in
> reference types.
>
> This could by itself be a very useful shortcut, but should probably be
> called out explicitly in the proposal just to remove any possible confusion
> of intent. As per my original question, it's most certainly obvious how
> this would be handled. I'm also not sure if this is usually the most
> desirable outcome.
>
> I think it would be more logical as a developer if your optionals remain
> optional within the block, but with the difference that they aren't
> strongly captured, and as such might become nil by the time the block
> executes, even if they weren't at the time of creation. Removing the
> optionality might force a lot of [weak param]  statements that otherwise
> wouldn't be needed.
>
>
> The intent is to make this behave exactly as if you had written it out
> manually and had guarded all captured references.  I think it works when it
> changes the default.  It would be confusing to add special cases for things
> that are already optional.  If you don’t want guarded behavior you’ll just
> need to use the capture list as you showed.
>


Ok, I can buy that. Still feel like this should be explicitly called out.
It's literally equivalent to [weak X] + guard let x = x { else return } for
each and every captured reference, unless otherwise overridden in the
capture list.



>
>
>
>
>>
>>
>> David
>>
>>
>> On Wed, Feb 22, 2017 at 1:40 PM, Matthew Johnson <matthew at anandabits.com>
>> wrote:
>>
>>>
>>> On Feb 22, 2017, at 3:36 PM, David Hedbor via swift-evolution <
>>> swift-evolution at swift.org> wrote:
>>>
>>> I did read it, but I think I skimmed it a bit too fast. You're correct
>>> in that it essentially solves the same problem using a different syntax
>>> (more compact at that). I think when I initially read it, I parsed it as
>>> the method would return at any point if the objects were freed
>>> (mid-execution of the closure). Re-reading it, I see that the proposal is
>>> in fact identical in functionality to mine, just with a different syntax.
>>>
>>> Given that your proposal still allows for overriding the behavior on an
>>> individual basis, the same thing can be accomplished. I'll put my support
>>> behind your draft, rather than expending more time with mine. :)
>>>
>>>
>>> Thanks David, glad to hear it!
>>>
>>>
>>> Cheers,
>>>
>>> David
>>>
>>>
>>> On Wed, Feb 22, 2017 at 12:57 PM, Matthew Johnson <
>>> matthew at anandabits.com> wrote:
>>>
>>>> Hi David,
>>>>
>>>> I just shared a draft proposal to introduce guarded closures last week:
>>>> https://lists.swift.org/pipermail/swift-evolution/Week
>>>> -of-Mon-20170213/032478.html.  I think you would find it very
>>>> interesting.
>>>>
>>>> I considered including a new capture list specifier `guard` in this
>>>> proposal but decided against it.  Guarded behavior requires prefixing the
>>>> contents of the closure with a guard clause that returns immediately if the
>>>> guard is tripped.  This is a property of the closure as a whole, not of an
>>>> individual capture.  For that reason, I decided that allowing a `guard`
>>>> specifier for an individual capture would be inappropriate.
>>>>
>>>> Instead, a guarded closure has a guarded by default capture behavior
>>>> which can be overridden with `weak`, `unowned` or `strong` in the capture
>>>> list.  The thread on this proposal was relatively brief.  I plan to open a
>>>> PR soon after making a few minor modifications.
>>>>
>>>> Matthew
>>>>
>>>> On Feb 22, 2017, at 2:48 PM, David Hedbor via swift-evolution <
>>>> swift-evolution at swift.org> wrote:
>>>>
>>>> Hello,
>>>>
>>>> (apologies if this got sent twice - gmail and Apple mail seems to
>>>> confused as to what account the first mail was sent from)
>>>>
>>>> I’m new to this mailing list, but have read some archived messages, and
>>>> felt that this would be a reasonable subject to discuss. It’s somewhat
>>>> related to the recent posts about @selfsafae/@guarded but distinctly
>>>> different regardless.
>>>>
>>>>
>>>> Problem:
>>>>
>>>> It’s often desirable not to capture self in closures, but the syntax
>>>> for doing so adds significant boilerplate code for [weak self] or us unsafe
>>>> when used with [unowned self]. Typically you’d do something like this:
>>>>
>>>>   { [weak self] in    self?.execute() }
>>>>
>>>> This is simple enough but often doesn’t work:
>>>>
>>>> { [weak self] in self?.boolean = self?.calculateBoolean() ]
>>>>
>>>> This fails because boolean is not an optional. This in turn leads to
>>>> code like this:
>>>>
>>>> { [weak self] in
>>>>    guard let strongSelf = self else { return }
>>>>    strongSelf.boolean = self.calculateBoolean()  }
>>>>
>>>> And this is the boilerplate code. My suggestion is to add a syntax that
>>>> works the same as the third syntax, yet doesn’t require the boilerplate
>>>> code.
>>>>
>>>>
>>>> Solution:
>>>>
>>>> Instead of using unowned or weak, let’s use guard/guarded syntax:
>>>>
>>>>
>>>> { [guard self] in
>>>>    self.isExecuted = self.onlyIfWeakSelfWasCaptured()
>>>> }
>>>>
>>>> In essence, guarded self is equivalent to a weak self, that’s captured
>>>> when the closure is executed. If it was already released at that point, the
>>>> closure is simply not executed. It’s equivalent to:
>>>>
>>>> { [weak self] in
>>>>    guard let strongSelf = self else { return }
>>>>    strongSelf.isExecuted = strongSelf.onlyIfWeakSelfWasCaptured()
>>>> }
>>>>
>>>> Except with a lot less boilerplate code, while not losing any clarify
>>>> in what it does.
>>>>
>>>> Impact / compatibility:
>>>>
>>>> This is simply additive syntax, and wouldn’t affect any existing code.
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>>
>>>>
>>>>
>>> _______________________________________________
>>> swift-evolution mailing list
>>> swift-evolution at swift.org
>>> https://lists.swift.org/mailman/listinfo/swift-evolution
>>>
>>>
>>>
>>
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170222/45521048/attachment.html>


More information about the swift-evolution mailing list