> On Feb 19, 2017, at 2:57 PM, Matthew Johnson via swift-evolution 
> <swift-evolution@swift.org> wrote:
> 
> A guarded closure may be created by prefixing a bound instance method 
> reference with the `?` sigil:
> 
> ```swift
> let guarded = ?myObject.myMethod
> 
> // desugars to:
> let guarded = { [weak myObject] in
>  guard let myObejct = myObject else {
>    return
>  }
>  myObject.myMethod()
> }
> ```

I like this in principle, but I don't like the syntax. The `?` is cryptic and 
looks pretty strange in prefix position. I think what I'd like to see is:

        let guarded = weak myObject.myMethod

Or:

        let guarded = weak(myObject).myMethod

This second alternative could give us the opportunity to specify a default 
return value if we'd like:

        let guarded = weak(myObject, else: nil).myMethod

> A guarded closure may be created by prefixing an inline closure with the `?` 
> sigil:
> 
> ```swift
> let guarded = ?{ flag in
>  flag ? someMethod() : someOtherMethod()
> }
> 
> // desugars to:
> let guarded = { [weak self] flag in
>  guard let strongSelf = self else {
>    return
>  }
> 
>  flag ? strongSelf.someMethod() : strongSelf.someOtherMethod()
> ```

This runs into the same problem as previous suggestions for addressing the 
weak-strong dance: There's no particular reason to privilege this behavior over 
any other.

I think I'd prefer to use a slightly wordier two-part solution here:

        1. If the capture list is just `[weak]` (or `[unowned]`), that's the 
default for all variables.

        2. A `guard` with no condition—in other words, `guard else`—tries to 
strengthen all weakly captured variables and enters the `else` block if any 
fail. (Alternative: `guard let else`.)

That makes your example into:

        let guarded = { [weak] flag in 
                guard else { return }
                flag ? self.someMethod() : self.someOtherMethod()
        }

This is many more characters, but I think it's also much clearer about what's 
going on.

> #### Self reference in escaping guarded closures
> 
> Guarded closures do not extend the lifetime of any objects unless a `strong` 
> capture is specified in the capture list.  Because this is the case users are 
> not required to explicitly reference self using the `self.` prefix inside a 
> guarded closure.

I think this is a bad idea, because the implicit `self` problem still exists 
here—it just has a slightly different shape. Here, if you accidentally use 
`self` without realizing it, your closure mysteriously doesn't get executed. 
This misbehavior is at least easier to notice, but it's still going to be hard 
to track down, since it's caused by something invisible in the code.

> ### `@guarded`
> 
> This proposal introduces the ability for arguments of function type to be 
> annotated with the `@guarded` attribute.  When `@guarded` is used the caller 
> must supply a guarded or non-capturing closure as an argument.  This ensures 
> that users of the API think very carefully before providing an argument that 
> captures a strong reference, and requires them to explictly state their 
> intent when they wish to do so.

Even in this modified form, I think this is a bad idea. The person writing the 
closure is the one who knows whether they want it to extend objects' lifetimes. 
The library author accepting the closure has no idea whether the objects the 
closure captures are models it needs or controllers it shouldn't keep—or, for 
that matter, models it shouldn't keep or controllers it needs.

If we really think that this is a serious problem, we should change `@escaping` 
closures to always capture weak unless they're explicitly told to capture 
strong and be done with it. If that sounds like a hare-brained idea, then we 
shouldn't do it half the time and not do it the other half.

-- 
Brent Royal-Gordon
Architechies

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

Reply via email to