Swift5.5について調べる
Swift5.4がリリースされたばかりだが、5.5がかなりでかいアップデートになりそうなのでチェックしたい。
まとめ
SE-0293 Extend Property Wrappers to Function and Closure Parameters
関数の引数にプロパティラッパーを使えるようになる。SwiftUIがいい感じに使っていた。
Binding<Value>
が条件付きでRandomAccessCollection
に準拠するようになったので、List($texts)
のような書き方ができる。これまではこう書いてしまうとBinding<String>
の値しか取れなかったが、この機能のおかげで次のように書けるようになった。
List($texts) { $text
Text(text)
TextField("edit", text: $text)
}
SE-0295 Codable synthesis for enums with associated values
関連値付きEnumにCodableへの自動準拠が追加。パッと見嬉しいが、ラベルがついていない関連値を_0
,_1
のようにラベル付けするのでちょっと扱いがつらそう。
SE-0296 async/await
非同期な処理を同期的処理の中で書くためにasync
な関数という概念と、それを呼び出すためのawait
という構文を導入する。
SE-0298 Async/Await: Sequences
後日追記。
SE-0299 Extending Static Member Lookup in Generic Contexts
一定の条件でプロトコルにstaticなメソッドやプロパティを定義できる。SwiftUIでいい感じに使われていた。
// before
TextField("input here", text: $text)
.textFieldStyle(RoundedBorderTextFieldStyle())
// after
TextField("input here", text: $text)
.textFieldStyle(.roundedBorder)
SE-0300 Continuations for interfacing async tasks with synchronous code
後日追記。
Sendable
and @Sendable
closures
SE-0302
Sendable
という@_marker
なプロトコルが追加され、多くの値型やactor
がSendable
に準拠する。条件を満たすクロージャをSendable
として扱うようにする属性が@Sendable
と書かれるようになる(@escaping
の対応物?)。
@_marker
はマーカープロトコルという新概念で、コンパイルされたものには一切影響を与えない。しばらくはコンパイラ内部で使われるらしい。
属性の頭が大文字なことについては、プロトコルからuser-definedなattributeを作れるようにする、という将来的な展望から@sendable
ではなく@Sendable
という記号になった様子。
SE-0306 actors
actor
という参照型が追加され、class/actor/struct/enum
の四種構成になる。シンプルなタイトルに反してインパクトの大きさがやばい。
SE-0307 Allow interchangeable use of CGFloat and Double types
CGFloatとDoubleを相互に明示的な変換なく使えるようになる。シンプルに便利。
#if
for postfix member expressions
SE-0308
#if
をメソッドチェーンの内部で使えるようになる。SwiftUIなどでは便利そう。
SE-0309 Unlock existentials for all protocols
ついに全てのプロトコルをExistential Typeとして使えるようになる。つまりこういうコードが書けるようになる。とてもインパクトの大きい変更。
var value: Numeric = 100
value = 50.2
var collection: Collection = [0, 1, 2]
collection = Set([3, 4, 5])
あと、今まで地味に辛かった「一部のプロトコルで準拠を判定できない問題」も解決する。
if value is Numeric { /* ... */ }
SE-0310 Effectful Read-only Properties
async
だったりthrows
だったりするRead onlyなプロパティを作れるようになる。
Implemented In: Swift 5.5
となっているProposalはここで確認できる。
見たところ影響してきそうなのはこの辺。
- propertyWrapperを関数・クロージャの引数に使えるようになる。resultBuilderと見た目がごっちゃになりそうだけど大丈夫だろうか。
- 関連値付きenumが自動で
Codable
に準拠するようになる。
-
async
とawait
がくる。
- 特殊な条件で
protocol
に静的メンバを追加できる。
-
actor
がくる。
-
CGFloat
とDouble
の変換が不要になる。
非同期処理関連に疎いのでかなり勉強しないと辛そう。
Extending Static Member Lookup in Generic Contexts(SE-0299)は使いようによってかなり便利そう。leading dot syntaxを用いるためだけにenumを使っていたようなケースが減らせるかも。
Extend Property Wrappers to Function and Closure Parameters(SE-0293)はあまり嬉しさがわからない。関数の中ではproperty wrapperのsetterが無効化されるらしい。どう使うんだろう、これ。
Validate
系は若干便利そうだけど、パフォーマンスが気になる場面で使っていいのか疑問。
SE-0295、Codable synthesis for enums with associated valuesはForumの論戦をなんとなく眺めていた記憶。
enum Command: Codable {
case load(String)
case store(key: String, Int)
}
の値がそれぞれ_0
と_1
でエンコードされるのは確かにうーーんという感じ。
{
"load": {
"_0": "MyKey"
}
}
{
"store": {
"key": "MyKey",
"_1": 42
}
}
少なくともすでに存在している明示的な実装をこれで置き換えることは無理っぽい。後々明示的な実装をせざるを得なくなった場面で泣きを見そうな感。
SE-0307、Allow interchangeable use of CGFloat
and Double
typesはシンプルに便利そう。
SE-0296、async/awaitはちゃんと理解できていない。早く使ってみたい。
SE-0306、actors。コードしか読んでいないが、衝撃。えっ型の種類がclass/struct/actor/enumになるの?
actorは参照型で、actor isolationと呼ばれるルールに沿って操作される必要がある。
actor-isolated
なプロパティは(同期的には?)self
で参照されるactor
からしか読み取れない(参照型なのでsetterがnonmutatingになってしまう都合か?)。func
もsubscript
も同様。この状態がisolated
であり、このような挙動をさせないためには明示的にnonisolated
を付加する必要がある。
isolated
な宣言には非同期的にしか(await
での呼び出しでしか)アクセスできない。actor
はアクセスを「メールボックス」に保存し、順番に処理する(なのでデータレースが発生しない)。
同期的な関数であっても呼び出し側は非同期的に呼び出さなければいけない(これは納得感がある)。非同期的にプロパティをgetはできるが、setはできない。
actor
は暗黙にSendable
プロトコルに準拠する(SE-0302、後で調べる)。ということは、どちらかというとSendable
な参照型が欲しいというのが元のニーズなのかも。
これが面白くて、Sendable
じゃない型を返す関数のcross-actor
な呼び出しをブロックすることで対処するらしい。なるほど。
if let primary = await account.primaryOwner() {
primary.name = "The Honorable " + primary.name // problem: concurrent mutation of actor-isolated state
}
@Sendable
なクロージャという話もあったが、一旦読み飛ばす。@sendable
ではなく@Sendable
と書かれているのがちょっと引っかかる。
reentrancyをサポートした場合デッドロックを避けられないという話(?)
良く理解できなかった。
SendableでAnyObjectなprotocolとしてActor
が追加される。
大筋はだいたい読んだが、そもそも大した並行性と向き合ったことがないのであまり嬉しさはわからず。
SE-0302はAcceptedだが実装はまだされていない様子?(実装された)
どのみちactor
が来るならSendable
も来るでしょう。気になるのはやはり@Sendable
というマーク。小文字始まりじゃないのか?
We want Swift programmers to get a static compiler error when they try to pass across concurrency domains that could introduce unprotected shared mutable state.
なるほど、Swiftの並行処理関連の根本的なモチベはここなのか。
ようやく全体読み終えた。Sendable
でない値をクロージャがキャプチャするのをどう防ぐのか、みたいな話だと理解した(あってる??)
基本lowercaseで書かれていたSwiftの属性が@Sendable
と書かれていることに強烈な違和感があるのだけど、特にその説明はなく。まじか。。。
Forumのスレッドで記述があった。Core teamがSE-0302のアクセプトの際に追加で@sendable
ではなく@Sendable
を用いることを提案したっぽい。
意味的には「クロージャ(関数)のキャプチャする値がSendable
である場合に、クロージャ自身もSendable
である」ことを表現するのが@Sendable
という表記らしい。将来的にはCodable
とかHashable
でも同様のことをできるようにするのを織り込んだ表現。
Just to function types. @A @B (Int) -> Float means "the type of functions from Int to Float that conform to the protocols A and B". It is a different type from (Int) -> Float, although it is a natural subtype. This is a way of creating restricted function types, which is different from the potential ability to make unrestricted function types conform to protocols.
これはより一般の自動適合でも同じようなことを可能にしようとしているのか、そういうことではないのか?
ResultBuilderにしてもConformanceBuilderにしても、そろそろVariadic Genericsを導入しないと辛いんじゃないかという気がするけど……。
Actor、継承はないのか。
あとSwift5.5で来そうでデカイのはSE-0309のUnlock existentials for all protocols。今はレビュー中だけどおそらくAcceptされるので、そうすると抽象型をかなり多くのケースで使えるようになる。any Protocol
も一緒に来て欲しい。
any Protocol
については大筋肯定的に捉えられていつつ、一部で反対意見がある様子。proposalはいつ出るんだろうか。
ここ数日で考えが変わりany Protocol
には反対したくなった。SE-0309自体は無事にAcceptされたっぽい。
any Protocol
についても「we don't want to prematurely introduce a new existential syntax」とのことなので、しばらくは安心できそう。
On this topic, the Core Team does not think this is the point to introduce a new existential syntax. We see eliminating distinctions between protocols with and without associated types as an important goal of this proposal, and imposing different syntax rules for different existentials would run counter to that goal. We also know that the existential syntax will have to be expanded to allow for where constraints on associated types, and we don't want to prematurely introduce a new existential syntax without knowing how that new syntax will accommodate other necessary extensions.
これもやはりSwift5.5で来ることになったっぽい
結局来ないのか
そうこうしているうちにImplementedなproposalが1つ増えた。
これは実際嬉しいけれども本質的にはもっと辛い問題な気がする。メソッドチェーンで書いた場合、条件分岐をうまく書けず、引数で三項演算子を用いるか、あるいはコピペでif
で書くか。どちらにしても保守性が悪化する。
嘘をつきました。
extension View {
@ViewBuilder func `if`<T: View, U: View>(_ condition: @autoclosure () -> Bool, then thenModifier: (Self) -> T, else elseModifier: (Self) -> U) -> some View {
if condition() {
thenModifier(self)
} else {
elseModifier(self)
}
}
@ViewBuilder func `if`<T: View>(_ condition: @autoclosure () -> Bool, then thenModifier: (Self) -> T) -> some View {
if condition() {
thenModifier(self)
} else {
self
}
}
}
struct IfDotTestView: View {
var body: some View {
Text("Hello")
.if(true) {
$0.foregroundColor(.red)
} else: {
$0.foregroundColor(.blue)
}
Text("Goodbye")
.if(false){
$0.foregroundColor(.green)
}
}
}
引数でのproperty wrapperの利用に@Sendable
の追加で「@カオス」がさらに増したような気がする。
- 属性、
@escaping
,@dynamicMemberLookup
,@autoclosure
,@unknown
など。型定義の前、case
の前、関数型の前、その他あちこちに出てくる。 - property wrapper、
@State
,@Binding
,@ObservedObject
など。変数宣言の前、引数の型定義の前に出てくる。 - result builder、
@ViewBuilder
など。関数の引数のクロージャの宣言の前や、関数や変数の宣言の前に出てくる。 - (new) protocol conformance、
@Sendable
(など)。(今のところ)関数の引数のクロージャの宣言の前に出てくる。
もともと命名規則もあやふやだった(プレフィックスがついたり、camelCaseだったり、snake_caseだったり)うえに、何を@属性
にするのかもイマイチよくわからない。callAsFunction
の時にも@callable
にするとかしないとかで揉めていたらしいけど……。ドキュメントにそのあたりの記述が見つけられない。
それぞれの機能は良いのだけど、そんなに@
を使いたいモチベーションがどこにあるんだろう。丸くて可愛いからか?名前を分離してほしい。
SE-0316、Global Actorが追加される場合さらに@globalActor
という属性が追加され、これでアノテーションした型が@MainActor
のように使える。これもproperty wrapperっぽい。
また1つ増えた。これも並行処理関連が理解できていないと辛そうなのでとりあえず他のものと一緒に考えたい感じかな。
SE-0298、Async/Await: Sequences。まだ確認していなかった。
SE-0300、Continuations for interfacing async tasks with synchronous code。今度読む。