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 anError
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 anactor
's state. In fact,async
properties prevent users from accidentally blocking on a computed property, becauseasync
requires that the users of the property take its possible suspension into account (this is explained in theAVAsynchronousKeyValueLoading
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.