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

David Hedbor neotron at gmail.com
Wed Feb 22 17:44:31 CST 2017


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

>
>
> Sent from my iPad
>
> On Feb 22, 2017, at 5:28 PM, David Hedbor <neotron at gmail.com> wrote:
>
>
>
> 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.
>
>
> The proposal does demonstrate this but I'll give it another look.  It
> sounds like it's not as clear as it could be.
>
>

Indeed it does, but with a non-optional reference. It's entirely plausible
that it's obvious to most, and that I'm overthinking it. Still doesn't hurt
to add a few lines specifically addressing it I think.





>
>
>>
>>
>>
>>
>>>
>>>
>>> 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/8ebed1b7/attachment.html>


More information about the swift-evolution mailing list