Introduction
本記事はSE-0310 Effectful Read-only Properties(d0e50d9時点)を紹介します。正確には、SE-0310内でサラッと流されてしまっている用語や説明を深掘りします。なので本筋の説明はありません。
このプロポーザルのタイトルを日本語に訳すと「効果を伴う読取専用のプロパティ」です。要はこれのことです:
var lastTransaction: Transaction { // getしかない。setは持ってない。
get async throws { // asyncとthrowsの"効果"を持つ。
}
}
このプロポーザルは前提にSE-0304 Async/awaitがあり、SE-0296などの関連プロポーザルと共にSwift 5.5から = Swift Concurrencyデビュー当時から導入されています。
勝手に読み心地を評価するとこんな感じ
| 項目 | 星 | コメント |
|---|---|---|
| 難易度 | ★★☆☆☆ | プログラミング言語というものに慣れている必要があります。 |
| 長さ | ★☆☆☆☆ | 402行のマークダウン。短いのでサクッと読めるでしょう。 |
| 重要度 | ★★★☆☆ | たまに本質を突いたことを言ってる気がするので星3にします。 |
このプロポーザルを読まなくても get async で困ることはありません。が、Swiftにおけるeffectの定義などが書いてあるので無視して通り過ぎるのはもったいないかも……
Motivation
この記事はSwift Advent Calendar 2025用に書かれました。
個人的なモチベはこちら:
https://speakerdeck.com/s_shimotori/asyncsequence-and-asyncstream?slide=4
いろいろ深堀りする!
Nominal type
Nominal types such as classes, structs, and enums in Swift support computed properties and subscripts, which are members of the type that invoke programmer-specified computations when getting or setting them.
Swiftのクラス、構造体、列挙体といった公称型は、取得や設定の際にプログラマが指定した計算をするメンバである計算プロパティとサブスクリプトをサポートしている。
Nominal typeという用語はSwiftに限らない一般的な用語です:
| 英語 | 日本語訳 |
|---|---|
| Nominal type | 公称型、名前的型 |
| Structural type | 構造的型 |
Swiftの公式ドキュメントではNominal typeのことを次のように説明しています:
In Swift, a type is considered a nominal type if it has been explicitly named by a declaration somewhere in code. Examples of nominal types include classes, structures and enumerations. Nominal types are an important concept in Swift because they may conform to protocols, be extended, and have values created using the initializer syntax
MyType().
Swiftでは、コード内のどこかの宣言で明示的に命名されている型は公称型と見なされる。公称型の例にはクラス、構造体、列挙体が含まれる。公称型はプロトコルに準拠したり拡張したりイニシャライザ構文MyType()で値を作成したりできるので、Swiftにおいては重要な概念である。
Nominal types (NominalTypes) | Documentation
このドキュメントにはStructural typeは出てきませんが、代わりにNon-nominal typeという用語が出てきます:
In contrast, non-nominal types do not have these capabilities. Many are obtained by composing other types. Examples include function types like
(Int) -> (String), tuple types like(Int, String), metatypes likeInt.Type, and special types likeAnyandAnyObject.
一方、非公称型はこれらの機能を持たない。多くは他の型を組み合わせることで得られる。例として(Int) -> (String)のような関数型や(Int, String)のようなタプル型、Int.Typeのようなメタ型、そしてAnyやAnyObjectのような特殊型が含まれる。
Nominal types (NominalTypes) | Documentation
Any や AnyObject に目をつむれば、Structural typeと同じように見えます。
SwiftにはNominal typeと似た用語としてNamed typeとCompound typeがあり、 Any と AnyObject に目をつむればやはり同じに見えます。
| 英語 | 日本語訳 |
|---|---|
| Named type | 名前付き型 |
| Compound type | 複合型 |
詳しくはこの辺を読むとよいです。
Effect
An effect is an observable behavior of a function. Swift's type system tracks a few kinds of effects
効果とは関数の観測可能な振る舞いである。Swiftの型システムはいくつかの種類の効果を追跡している
唐突にeffectの定義が出てくるのでSwift Evolution内だけの用語に見えますが、WWDCのSwiftの動画でも登場します。Meet Swift Async Algorithms - WWDC22 - Videos - Apple Developerとか。
API Design Guidelines
SE-0310にはAPI Design Guidelinesからの引用が載っており、当該ガイドラインを見に行くと確かに引用元の文章が見つかります:
Document the complexity of any computed property that is not O(1). People often assume that property access involves no significant computation, because they have stored properties as a mental model. Be sure to alert them when that assumption may be violated.
O(1)でない計算プロパティの複雑性を示す。 プログラマは、格納型プロパティをメンタルモデルとして持っているため、プロパティアクセスには大きな計算が伴わないとしばしば想定する。その想定が破られるかもしれないときは必ず注意を促すこと。
API Design GuidelinesのGeneral Conventions
- Complexity: と async で複雑性を示せということみたいです。
c.f. メンタルモデル - Wikipedia
SE-NNNN Throwing Properties and Subscripts
SE-0310は『Throwing Properties and Subscripts』なるプロポーザルにも触れています。これはBecca Royal-GordonさんというAppleの方が書いたpitchで、swiftlang/swift-evolutionには存在しないみたいです。
実はコメントでも触れられている……。
<!-- realistic uses of `throw` in properties have been [detailed in a prior pitch](https://github.com/beccadax/swift-evolution/blob/72c55f33b94749e22637bd8277661599e9cd8007/proposals/0000-throwing-properties.md) -->
AVFoundationの例
SE-0310はAVAsynchronousKeyValueLoadingを槍玉にあげています。
iOS 26.0現在の AVAsynchronousKeyValueLoading はSwift 6に追従してだいぶモダンになっています。結局 get async にはなっていません。
| メソッド | 対応バージョン |
|---|---|
load(_:isolation:)load(_:_:_:isolation:)
|
iOS 26.0からiOS 15.0+へのバックデプロイ |
loadValuesAsynchronously(forKeys:completionHandler:) |
iOS 4.0–16.0 Deprecated |
declareとdefine
Additionally, one can declare (but not define) an effectful property (such as for a protocol) by adding the effect keywords following the
get
さらに、getに続けて効果キーワードを追加することで、効果を伴うプロパティを(プロトコルなどのために)(定義ではなく)宣言できる
SE-0310ではdeclareとdefineが明確に区別されています。
違いはこの辺に書いてあります:
Declarations | Documentation(原文)
宣言(Declarations) · The Swift Programming Language日本語版(日本語訳)
SE-0310文中で G とか effects(G) とか話している箇所は両者の違いがわかりやすい例かもしれない。
witness
For example, one can write:
(source code)
to enforce that a type conforming toAccountprovides property and subscript witnesses that have the same or fewer effects than what is allowed by the protocol.
例えばこのように書ける:
(ソースコード)
Accountへ準拠している型に対し、プロトコルで許されているものと同じかそれ以下の効果を持つプロパティとサブスクリプトのwitnessを提示するように強制する。
意訳すると、プロトコル(ここでは例として protocol Account が使われている)へ準拠するためにプロパティを実装しようというときに、プロトコル要件にない効果(例でいうと subscript(_:) には throws 効果がない)を勝手に付け加えたら、それは準拠したことにならないよってことです。
"witness"のところを意訳すると実装や定義になるのですが、witnessはれっきとしたSwiftの用語なので別の単語に置き換えてしまうと正確ではありません。witnessはwitnessです。
witnessについては以下で触れられています:
- SE-0306 Actors
- Where does the term witness table come from? - Compiler - Swift Forums
- Understanding Swift Performance - WWDC16 - Videos - Apple Developer
Swift and Haskell both borrow this term from a common source, constructive logic, where proof terms serve as witnesses for their propositions and, in particular, where you prove an existential proposition
∃x:T . P(x)by producing anxwhich satisfiesPand thus serves as a witness to the truth that such anxdoes indeed exist.
You can think of a protocol requirement as the proposition "there exists a declaration which satisfies this", for which the only possible constructive proof is a witness: a concrete declaration that satisfies it.
SwiftとHaskellはどちらもこの用語を共通の出典である構成的論理から借用しており、そこでは命題に対する証人(witness)として証明項が機能する。特に存在命題∃x:T. P(x)は、Pを満たすことでそのようなxが実際に存在することの証人として機能するxを提示して証明される。
プロトコル要件は"これを満たす宣言が存在する"という命題として考えることができ、可能性がある唯一の構成的な証明は証人:それを満たす具体的な実装である。
Where does the term witness table come from? - Compiler - Swift Forums
Haskellにもwitnessという用語があり、「ある型が型制約を充足できるか(目当てのメソッドが全て実装されているか)どうかという命題があり、その証人/証拠(その型に実装されている具体的なメソッド)のことを英語でwitnessと言っている」っぽい。たぶん。
witnessの日本語訳
たぶん存在しません。みんなwitness tableのことをそのままウィットネステーブルと呼んでそう。
Understanding Swift Performanceのキャプションの訳語をパクろうにも古い動画なので日本語のキャプションがついていません。ちなみに简体中文キャプションでは
The answer to this question is a table based mechanism called the Protocol Witness Table.
这个问题的答案是一个基于表的机制 叫协议证明表
と訳されています。protocol = 协议、witness = 证明、table = 表、です。
つまりwitnessは漢字で書くと証明。
Objective-C bridging
SE-0310は例としてSFSafariTabとENManagerを挙げています。
| メソッド | 対応バージョン |
|---|---|
getPagesWithCompletionHandler:getPagesWithCompletionHandler(_:)pages()
|
macOS 10.12+ |
getUserTraveledWithCompletionHandler:getUserTraveled(completionHandler:)userTraveled()
|
iOS 12.5+ |
結局 var pages や var userTraveled はありません。例として挙げられちゃっただけということですね。
効果を伴う設定可能なプロパティ
_modify
_modify はOwnershipManifestoに出てくる modify と同一人物です。たぶん。
- swift/docs/OwnershipManifesto.md at main · swiftlang/swift
- Swiftの_modify {}, _read {}について調べた #iOS - Qiita
setter について
SE-0296には次のように書かれています:
Special functions like
deinitand storage accessors (i.e., the getters and setters for properties and subscripts) cannot beasync.Rationale: Properties and subscripts that only have a getter could potentially be
async. However, properties and subscripts that also have anasyncsetter imply the ability to pass the reference asinoutand drill down into the properties of that property itself, which depends on the setter effectively being an "instantaneous" (synchronous, non-throwing) operation. Prohibitingasyncproperties is a simpler rule than only allowing get-onlyasyncproperties and subscripts.
deinitやストレージアクセサ(つまりプロパティやサブスクリプトに対するgetterとsetter)のような特殊な関数はasyncにできない。論拠:getterのみを持つプロパティやサブスクリプトは潜在的に
asyncにできる可能性がある。しかし、asyncsetterも持つプロパティやサブスクリプトは、参照をinoutとして渡してプロパティ自体の性質を掘り下げる能力があることを暗示する。これはsetterが事実上"即座の"(同期的で、スローしない)操作であることに依存する。getのみのasyncプロパティやサブスクリプトだけを認めるよりはasyncプロパティを禁止する方が単純なルールである。
SE-0296時点では関数のみが async を持てましたが、現在は
-
get async:SE-0310でできた -
set async:つらい -
deinit async:isolated deinitに先を越された(SE-0371 Isolated synchronous deinitの中でasyncにも言及あり)
となっています。 deinit async の明日はどっちだ。
まとめ
小難しいことが書いてあるように見えますが、 get async の話自体に特に新情報とかひねった話はないです。