<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#'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'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 "context manager".</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'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 <resources>` "suffix".</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'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>