SE-0310: Effectful Read-only Properties

Effectful properties and subscripts do not inherently break this time-complexity assumption. Effects specifiers on a get accessor only indicate what might happen during the access that the use-sites must take into account:

  • For throws, the user must be prepared for an Error value to be returned instead of the expected value.
  • For async, the user must be within an asynchronous context, because the accessor may require a suspension of the current task to produce a result, for example, to gain exclusive access to an actor's state. In fact, async properties prevent users from accidentally blocking on a computed property, because async requires that the users of the property take its possible suspension into account (this is explained in the AVAsynchronousKeyValueLoading example).

It's still up to the programmer to stay within the guidelines that users expect, an amortized O(1) time for the accessor, regardless of the effects the accessor may exhibit. This is achieved in the usual way for any non-trivial computed property or subscript, by returning a cached answer when possible.

To expand a bit on one of the examples in the proposal: for AVAsynchronousKeyValueLoading, which has AVAsset as one of its conformers. One of AVAsset's potentially blocking computed properties is var duration: CMTime. If the asset isn't downloaded already, it will block, so it's still O(1) amortized. Should that property instead be the method func getDuration() -> CMTime instead? I think that's a stylistic decision that is up the API author.

Of course, if it's not possible to cache the result and a non-trivial computation is always required, then a computed property or subscript is indeed not a good choice. Edit: well, possibly not a good choice. For example, subscript on various data structures is not O(1) and that is a reasonable expectation. Chris also pointed out var count as well.

6 Likes