<div dir="ltr"># Introduction<div><br></div><div>Add a new `Scoped` protocol and enhance the do statement to automatically call enter/exit actions on resources.<br><div><br></div><div># Motivation</div><div><br></div><div>Resources (e.g., locks, files, sockets, etc.) often need to be scoped to a block, where some action is taken at the start of the block and another is required at the end. Examples include locking and unlocking a lock in a critical section or closing a file at the end of a block.<br></div><div><br></div><div>Doing this manually is possible using `defer` statements among other options, but this is error prone as a `defer` can be forgotten, `lock`/`unlock` calls for two locks can be switched due to a typo, etc. Having a dedicated language construct for this common case makes it easier to read and write while making code shorter and clearer.</div><div><br></div><div># Language Survey</div><div><br></div><div>At least three major languages have widely used statements for this use case.</div><div><br></div><div>## C#</div><div><br></div><div>C# has the `using` statement and the associated `IDisposable` interface.</div><div><br></div><div>```csharp</div><div><div>using (StreamReader sr = new StreamReader(filename)) {</div><div>    txt = sr.ReadToEnd();</div><div>}</div></div><div>```</div><div><br></div><div>C#&#39;s solution only handles close/exit actions via the `IDisposable.Dispose()` method and so cannot easily be used with items such as locks; however, C# has the additional `lock` statement for that use case.</div><div><br></div><div>## Java</div><div><br></div><div>Java has try-with-resources and the associated `AutoCloseable` interface.</div><div><br></div><div>```java</div><div><div>try (BufferedReader br = new BufferedReader(new FileReader(path))) {</div><div>    return br.readLine();</div><div>}</div></div><div>```</div><div><br></div><div>Java&#39;s solution only handles close/exit actions via the `AutoCloseable.close()` method and so cannot easily be used with items such as locks; however, Java has the additional `synchronized` statement for that use case.</div><div><br></div><div>## Python</div><div><br></div><div>Python has with `with` statement and the associated `__enter__` and `__exit__` special methods that classes may implement to become a &quot;context manager&quot;.</div><div><br></div><div>```python</div><div>with lock, open(path) as my_file:</div><div>    contents = my_file.read()</div><div>    # use contents</div><div>```</div><div><br></div><div>Python&#39;s solution handles both enter and exit actions and so this one construct is usable for locks as well as resources like sockets and files.</div><div><br></div><div># Proposed Solution</div><div><br></div><div>We add a new protocol called `Scoped` to the standard library. Types for resources that have enter/exit actions will be extended to add conformance to this protocol.</div><div><br></div><div>The `do` statement will be extended to allow a new `using &lt;resources&gt;` &quot;suffix&quot;.</div><div><br></div><div># Detailed Design</div><div><br></div><div>The `Scoped` protocol shall be as follows:</div><div><br></div><div>```swift</div><div><div>public protocol Scoped {</div><div>    func enterScope()</div><div>    func exitScope()</div><div>}</div></div><div>```</div><div><br></div><div>The compiler statement will accept a new form for resources. For example,</div><div><br></div><div>```swift</div><div>do using lock, let file = try getFileHandle() {</div><div>    // statements</div><div>}</div><div>```</div><div><br></div><div>As can be seen in the example above, the resources can be bindings that already exist (like `lock`) or can be new bindings. Bindings created with `do using` are not available outside of the scope of the `do using`. Only types conforming to `Scoped` may be using with `do using`. Use of non-conforming types will result in a compiler error.</div><div><br></div><div>The above example would be syntactic sugar for the following:</div><div><br></div><div>```swift</div><div><div>do {</div><div>    lock.enterScope()</div><div>    defer { lock.exitScope() }</div><div><br></div><div>    let file = try getFileHandle()</div><div>    file.enterScope()</div><div>    defer { file.exitScope() }</div><div><br></div><div>    // statements</div><div>}</div></div><div>```</div><div><br></div><div># Framework Changes / Examples</div><div><br></div><div>As an example of some real-world classes that would be useful with `Scoped`, from Foundation:<br><br>```swift<br><div>// Would be nice to extend the NSLocking protocol instead, but that&#39;s not supported yet.</div><div>extension NSLock: Scoped {</div><div>    func enterScope() {</div><div>        self.lock()</div><div>    }</div><div><br></div><div>    func exitScope() {</div><div>        self.unlock()</div><div>    }</div><div>}</div><div><br></div><div>extension NSFileHandle: Scoped {</div><div>    func enterScope() {}</div><div><br></div><div>    func exitScope() {</div><div>        self.closeFile()</div><div>    }</div><div>}</div></div><div>```</div><div><br></div><div># Questions and Concerns</div><div> * Bikeshedding protocol naming and scoping syntax</div><div> * Should the enter and exit actions be allowed to throw errors?</div><div><div><br></div>-- <br><div class="gmail_signature">Trent Nadeau</div>
</div></div></div>