> On 22 May 2016, David Hart wrote:
> 
> If the design team is very serious about not integrating optional warnings, 
> then I don’t think it is a huge bother to implement think in linters like 
> SwiftLint is doing.

I'm fine with the way SE-0009 was decided but I think the review left one 
consideration for linters unexplored: How to be explicit about what's captured 
by a closure if the coding style enforced by a linter involves using `self.` 
everywhere?

There were (are) basically two reasons for keeping `self.` implicit the way it 
is:

Reason 1. It keeps noise level down. E.g. computed properties often read better 
this way:

    var area: Double {
      return width * height
      // rather than: 'self.width * self.height'
    }

Reason 2. It makes the capturing of `self` explicit, because `self.` is only 
required in escaping closures and thus the capturing expressions require 
thinking when writing code and also stand out when reading code.

I think those are good reasons. But then, the language rules don't really 
favour the other coding style where the `self.` prefix is used throughout, even 
if it *can* be enforced by a linter:

Example 1. There's no other way (but using the `self.` prefix) to indicate that 
`self` should be retained by a closure:

    self.prepareWork()
    queue.async { [self] in
    //             ^
    // error: Expected 'weak', 'unowned', or no
    // specifier in capture list
      self.doTheWork()
    }

Example 2. There's currently no way to mark when an escaping closure is 
intended to **not** capture any other references but those explicitly listed:

    queue.async { [bar] in
      if bar.isAdjustable {
        baz.adjust()
      }
    }
    // Meant 'bar', but compiler didn't alert!

So I think it would be a good idea to adjust the capture list syntax a bit:

Suggestion 1. Allow capture lists to explicitly state that they capture `self` 
strongly by spelling it out with no weak/unowned specifier, i.e. `[self]`.

Suggestion 2. Add a succinct way to indicate that the capture list is 
*comprehensive*, i.e. that implicitly capturing other variables from the local 
scope is an error. (Capturing variables from the file scope should be allowed 
though, I reckon.) The syntax for this could be e.g. postfixing the capture 
list brackets with the exclamation mark `!`:

    queue.async { [service]! in
      service.handleTask(self.task)
    //                   ^
    // error: Implicit capture of 'self' in closure
    }
    queue.async { [service, self]! in
      service.execute(self.task) // ok
    }
    queue.async { [service, task = self.task]! in
      service.execute(task) // also ok; didn't capture 'self'
    }
    queue.async { [bar]! in
      if bar.isAdjustable {
        baz.adjust()
    //  ^
    // error: Implicit capture of 'baz' in closure
      }
    }

With these two changes, the coding style choice of what to use the `self.` 
prefix for would be better supported both ways, and no optional warnings would 
be needed. A linter could then require capturing `self` explicitly where it's 
used inside an escaping block. Myself, I wouldn't use comprehensive capture 
lists all the time but there have been a few cases where it would've been 
useful to prevent mistakenly capturing anything that could create a retain 
cycle.

Any thoughts? Would an idea like this help any of the people who started this 
mailing list thread—that is, with the aid of a respectively configured linter 
of course?

— Pyry

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to