Closed44

Swift5.5について調べる

Miwa / EnsanMiwa / Ensan

Swift5.4がリリースされたばかりだが、5.5がかなりでかいアップデートになりそうなのでチェックしたい。

Miwa / EnsanMiwa / Ensan

まとめ

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

後日追記。

SE-0302 Sendable and @Sendable closures

Sendableという@_markerなプロトコルが追加され、多くの値型やactorSendableに準拠する。条件を満たすクロージャを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を相互に明示的な変換なく使えるようになる。シンプルに便利。

SE-0308 #if for postfix member expressions

#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なプロパティを作れるようになる。

Miwa / EnsanMiwa / Ensan

見たところ影響してきそうなのはこの辺。

  • propertyWrapperを関数・クロージャの引数に使えるようになる。resultBuilderと見た目がごっちゃになりそうだけど大丈夫だろうか。

https://github.com/apple/swift-evolution/blob/main/proposals/0293-extend-property-wrappers-to-function-and-closure-parameters.md

  • 関連値付きenumが自動でCodableに準拠するようになる。

https://github.com/apple/swift-evolution/blob/main/proposals/0295-codable-synthesis-for-enums-with-associated-values.md

  • asyncawaitがくる。

https://github.com/apple/swift-evolution/blob/main/proposals/0296-async-await.md

  • 特殊な条件でprotocolに静的メンバを追加できる。

https://github.com/apple/swift-evolution/blob/main/proposals/0299-extend-generic-static-member-lookup.md

  • actorがくる。

https://github.com/apple/swift-evolution/blob/main/proposals/0306-actors.md

  • CGFloatDoubleの変換が不要になる。

https://github.com/apple/swift-evolution/blob/main/proposals/0307-allow-interchangeable-use-of-double-cgfloat-types.md

Miwa / EnsanMiwa / Ensan

Extending Static Member Lookup in Generic Contexts(SE-0299)は使いようによってかなり便利そう。leading dot syntaxを用いるためだけにenumを使っていたようなケースが減らせるかも。

Miwa / EnsanMiwa / Ensan

Extend Property Wrappers to Function and Closure Parameters(SE-0293)はあまり嬉しさがわからない。関数の中ではproperty wrapperのsetterが無効化されるらしい。どう使うんだろう、これ。

Miwa / EnsanMiwa / Ensan

Validate系は若干便利そうだけど、パフォーマンスが気になる場面で使っていいのか疑問。

Miwa / EnsanMiwa / Ensan

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
  }
}

少なくともすでに存在している明示的な実装をこれで置き換えることは無理っぽい。後々明示的な実装をせざるを得なくなった場面で泣きを見そうな感。

Miwa / EnsanMiwa / Ensan

SE-0296、async/awaitはちゃんと理解できていない。早く使ってみたい。

https://qiita.com/koher/items/29357b5e00aec1962601
https://zenn.dev/koher/articles/swift6-concurrency
https://speakerdeck.com/koher/await

Miwa / EnsanMiwa / Ensan

Swift5.4でswift -Xfrontend -enable-experimental-concurrencyで動かすと一応動くっぽいが、謎のエラーで落ちた。

Miwa / EnsanMiwa / Ensan

SE-0306、actors。コードしか読んでいないが、衝撃。えっ型の種類がclass/struct/actor/enumになるの?

Miwa / EnsanMiwa / Ensan

actorは参照型で、actor isolationと呼ばれるルールに沿って操作される必要がある。
actor-isolatedなプロパティは(同期的には?)selfで参照されるactorからしか読み取れない(参照型なのでsetterがnonmutatingになってしまう都合か?)。funcsubscriptも同様。この状態がisolatedであり、このような挙動をさせないためには明示的にnonisolatedを付加する必要がある。

isolatedな宣言には非同期的にしか(awaitでの呼び出しでしか)アクセスできない。actorはアクセスを「メールボックス」に保存し、順番に処理する(なのでデータレースが発生しない)。
同期的な関数であっても呼び出し側は非同期的に呼び出さなければいけない(これは納得感がある)。非同期的にプロパティをgetはできるが、setはできない。

Miwa / EnsanMiwa / Ensan

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と書かれているのがちょっと引っかかる。

Miwa / EnsanMiwa / Ensan

reentrancyをサポートした場合デッドロックを避けられないという話(?)
良く理解できなかった。

Miwa / EnsanMiwa / Ensan

大筋はだいたい読んだが、そもそも大した並行性と向き合ったことがないのであまり嬉しさはわからず。

Miwa / EnsanMiwa / Ensan

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の並行処理関連の根本的なモチベはここなのか。

Miwa / EnsanMiwa / Ensan

ようやく全体読み終えた。Sendableでない値をクロージャがキャプチャするのをどう防ぐのか、みたいな話だと理解した(あってる??)

基本lowercaseで書かれていたSwiftの属性が@Sendableと書かれていることに強烈な違和感があるのだけど、特にその説明はなく。まじか。。。

Miwa / EnsanMiwa / Ensan

Forumのスレッドで記述があった。Core teamがSE-0302のアクセプトの際に追加で@sendableではなく@Sendableを用いることを提案したっぽい。

https://forums.swift.org/t/se-0302-second-review-sendable-and-sendable-closures/45253/62

意味的には「クロージャ(関数)のキャプチャする値が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.

これはより一般の自動適合でも同じようなことを可能にしようとしているのか、そういうことではないのか?

Miwa / EnsanMiwa / Ensan

ResultBuilderにしてもConformanceBuilderにしても、そろそろVariadic Genericsを導入しないと辛いんじゃないかという気がするけど……。

Miwa / EnsanMiwa / Ensan

あとSwift5.5で来そうでデカイのはSE-0309のUnlock existentials for all protocols。今はレビュー中だけどおそらくAcceptされるので、そうすると抽象型をかなり多くのケースで使えるようになる。any Protocolも一緒に来て欲しい。

https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md

Miwa / EnsanMiwa / Ensan

any Protocolについては大筋肯定的に捉えられていつつ、一部で反対意見がある様子。proposalはいつ出るんだろうか。

Miwa / EnsanMiwa / Ensan

ここ数日で考えが変わりany Protocolには反対したくなった。SE-0309自体は無事にAcceptされたっぽい。
any Protocolについても「we don't want to prematurely introduce a new existential syntax」とのことなので、しばらくは安心できそう。

https://forums.swift.org/t/accepted-se-0309-unlock-existentials-for-all-protocols/47902

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.

Miwa / EnsanMiwa / Ensan

そうこうしているうちにImplementedなproposalが1つ増えた。
https://github.com/apple/swift-evolution/blob/main/proposals/0308-postfix-if-config-expressions.md

Miwa / EnsanMiwa / Ensan

これは実際嬉しいけれども本質的にはもっと辛い問題な気がする。メソッドチェーンで書いた場合、条件分岐をうまく書けず、引数で三項演算子を用いるか、あるいはコピペでifで書くか。どちらにしても保守性が悪化する。

Miwa / EnsanMiwa / Ensan

嘘をつきました。

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)
            }
    }
}
Miwa / EnsanMiwa / Ensan

引数でのproperty wrapperの利用に@Sendableの追加で「@カオス」がさらに増したような気がする。

  • 属性、@escaping, @dynamicMemberLookup, @autoclosure, @unknownなど。型定義の前、caseの前、関数型の前、その他あちこちに出てくる。
  • property wrapper、@State, @Binding, @ObservedObjectなど。変数宣言の前、引数の型定義の前に出てくる。
  • result builder、@ViewBuilderなど。関数の引数のクロージャの宣言の前や、関数や変数の宣言の前に出てくる。
  • (new) protocol conformance、@Sendable(など)。(今のところ)関数の引数のクロージャの宣言の前に出てくる。
Miwa / EnsanMiwa / Ensan

もともと命名規則もあやふやだった(プレフィックスがついたり、camelCaseだったり、snake_caseだったり)うえに、何を@属性にするのかもイマイチよくわからない。callAsFunctionの時にも@callableにするとかしないとかで揉めていたらしいけど……。ドキュメントにそのあたりの記述が見つけられない。

Miwa / EnsanMiwa / Ensan

それぞれの機能は良いのだけど、そんなに@を使いたいモチベーションがどこにあるんだろう。丸くて可愛いからか?名前を分離してほしい。

Miwa / EnsanMiwa / Ensan

SE-0316、Global Actorが追加される場合さらに@globalActorという属性が追加され、これでアノテーションした型が@MainActorのように使える。これもproperty wrapperっぽい。

このスクラップは2022/02/11にクローズされました