[swift-evolution] [Review] SE-0073: Marking closures as executing exactly once

Pyry Jahkola pyry.jahkola at iki.fi
Wed May 4 01:28:01 CDT 2016


Here's my review of "SE-0073 <https://github.com/apple/swift-evolution/blob/master/proposals/0073-noescape-once.md>: Marking closures as executing exactly once".

> What is your evaluation of the proposal?

+1. I think this is a good idea and should be accepted (without extending the proposed scope).

However, I think the proposal should be more explicit about the case when (and whether) the block itself throws. Specifically, I think we should highlight that the criterion that

> it must not be executed on any path that throws


implies that a @noescape(once) parameter itself cannot throw (until another language change allows otherwise). Consider this use case:

    final class Database {
      final class Transaction { ... }
      // Not allowed by SE-0073:
      func transact(_ apply: @noescape(once) (Transaction) throws -> ()) rethrows
    }

    func incrementScore(db: Database, game: String) throws -> Int {
      let oldScore: Int
      // This use of "@noescape(once) ... throws" *could* be ok if the error was
      // unconditionally propagated out of the scope of uninitialised variables:
      try db.transact { tx in
        oldScore = try tx.read(key: "\(game).score")
        try tx.update(key: "\(game).score", value: oldScore + 1)
        try tx.update(key: "\(game).updatedAt", value: NSDate())
      }
      return oldScore
    }

Being able to throw out of a @noescape(once) block would be useful in cases like this, as it would naturally allow rolling back a transaction. But it would complicate the language by requiring that no one catches the error in the scope where uninitialised variables are defined. I suggest adding this remark to the Future directions.

> Is the problem being addressed significant enough to warrant a change to Swift?

Yes. I'm looking forward to being able to initialise immutable variables in dispatch_sync blocks and such without needing to resort to defining them as `var`.

> Does this proposal fit well with the feel and direction of Swift?

Yes it does, it aligns nicely with the trailing closure syntax that allows for the emulation of control flow constructs with library code. And as we know, the control flow constructs already allow the delayed initialisation of local variables.

> If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

With a similar feature, I have only used languages (in the sense of "used in anger") where the delayed initialisation is allowed by allowing an undefined state (e.g. C++). For a not-really-applicable-to-Swift alternative approach, Haskell's laziness allows the recursive definition of variables for a similar effect.

Finally, I think it's interesting that the requirement for @noescape(once) arguments to be unconditionally executed has a similarity to linear type systems (Wikipedia <https://en.wikipedia.org/wiki/Substructural_type_system>) and uniqueness or references. Except in this case, the reference is not to an object but a function. Such a function reference bears a certain similarity to the deinit of a uniquely held object. I think the proposed feature may later merge with a bigger language update that brings reference uniqueness to the type system.

> How much effort did you put into your review? A glance, a quick reading, or an in-depth study?


Between quick reading and in-depth study.

— Pyry

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20160504/9ba04e7c/attachment.html>


More information about the swift-evolution mailing list