Guarded closures

I think we are misunderstanding each other. I don't mean that you haven't replied to my post. To address or answer a concern would involve changing the proposed idea such that the concern becomes inapplicable.

The posts above mine in this thread lay out a variety of concerns, none of which have been addressed to date. Forward movement would involve giving deep consideration to these concerns and coming up with something new that resolves the difficulties raised. It appears that you're dismissing all of these comments as "very subjective," which is not conducive to having a discussion.

@xwu You are welcome to come with another keyword proposal instead of guard. As I said, guard makes sense for me. That is the only thing I can come up with in order to accomodate your concerns.

About the other concern you raised, I think is out of the scope of the proposal, as [guard self] (the way I see it) is trying to address a very concrete case: capturing weakly + not executing the code inside the closure if any of the captured identifiers are nil. Doing fatalError() does not fall in that category, because to halt with fatalError() you need to execute the code in the closure.

@xwu The creator of the more complex draft said that the increased complexity added by his proposal is not worth, going back to the original proposal of [guard self].

As him, I believe in introducing small proposals one after another, instead of making a big proposal that tries to fix all our concerns. The [guard self] proposal is a scoped change solving a concrete problem when using closures.

Some of the other posts before were presenting guard let self = self else { return } as a solution. I already explained here why the guard let self = self else { return } proposal seems different and independant of the [guard self] proposal to me, and I asked you if you agreed with me on that.

Other post said that:

I agree with that, but still believe that the benefits of it pays off the slightly increase of complexity.

About:

I agree. And I also commented that:

And the last post saying:

Aligns with your concern about the keyword guard, and the possibility of finding another keyword that fits better.

Multiple ways to achieve the same do not improve development experience. Constructs that helps to write a faulty, unsafe code also do not make language better. If there is some unsafe operation, it should be painful to use it. [guard self] is too simple, while making much more potential damage in compare with unowned. unowned terminates execution, [guard self] will just skip the step in a flow without a simple way to discover what actually happened and why. If developer try to do something potentially unsafe and hard to debug the process of doing so should be painful for developer. There is a benefit in a boilerplate for such operation, as more you do so, less you want such operations to happen in general and starts to discover other ways.

TLDR not every boilerplate need to be eliminated, some of it for the benefit of users.

4 Likes

Have we considered paving the cowpaths and making closures vend a weakSelf by default (or alternatively having a strongSelf and self that's default weak)? While I agree not all boilerplate is necessarily bad, in RxSwift environments where any action is a chain of closures, this completely loses all meaning and becomes typing these sequences over and over.

1 Like

I wrote a similar proposal that dealt with simplification of strongSelf pattern using the keyword "Required" instead of your "Guarded" (I will use Guard from now on in this posting). You came up with, and posted, the idea first. I only started talking about the idea with friends at the beginning of the 2018 year.

I did talk with a number of Swift engineers at WWDC 2018, in preparation to submitting the idea. Unanimously, they all were in favor of it. They also unanimously agreed that the code should be able to handle both a void result and an optional result (returning the appropriate Void or nil).

The one variation that was suggested was to not use the [quard self] syntax, but instead simply use keyword guard in from off the closures parameter:

let block = { guard self, guard x in
self.doit(with: x)
}

Rather than my original proposal (similar to yours) of:

let block = { [guard self, guard x], in
self.doit(with: x)
}

I hope this feedback helped. Either way, PLEASE go with this idea. We need it

I don't think that most programmers who are just trying to write apps (mostly) think to specifically about whether the keyword is for memory ownership or control flow. I think if you gave an example to developers without telling what it is, most could reason what it did.

There has to be a balance between pureness and practicality. The pureness keeps the language simple and allows you to reason about it. But practicality allows some shortcuts to be made.