cockscomblog?

cockscomb on hatena blog

WWDC22への期待が高まってきたのでSwift Evolutionをナナメ読みする

WWDC22で何が発表されるのか、期待で眠りが浅い日々を過ごしている。何かヒントはないかとSwiftの次期バージョンについて調べていた。

普段から、定期的に同僚のid:ikesyoid:yutailang0119id:nakiwoid:kouki_danらとSwift Evolutionの様子をチェックしていて、おもしろいプロポーザルにはだいたい目を通している(教えてもらっている)のだけど、あらためてみてみると見えてくるものもある。

Swift 5.7になる

現在のSwiftのバージョンは5.6で、次のバージョンはSwift 5.7となる。

Swift 6は破壊的変更のためにとってあるバージョンで、そういう意味ではPython 3のようなフィールもあって不気味だが、期待も高い。

Swiftは(Appleの都合もあってか)半年ごとにマイルストーンを刻んでいる。直近のバージョンで言えば、Swift 5.5でasync/awaitやActorが入って、非同期処理に対する言語的なサポートが充実した。Swift 5.6はそれと較べると小規模な変更で、「チックタック」の「タック」という趣があった。

ではSwift 5.7はどうなるのかというと、広範なアップデートがありそうに見える。

apple/swift-evolution

Swiftの言語に対する変更は、SwiftがOSSであり、Swift Evolutionによる民主的な意思決定プロセスを採用していることから、予め窺い知れることが多い。ということでSwift Evolutionの主なプロポーザルを見ていく。

ここで取り上げるプロポーザルは、すでにSwift 5.7で実装されることが明らかなものもあれば、まだ議論の途中のものもある。また受理されたプロポーザルであっても、実装がSwift 5.7に間に合わないこともある。それでもSwiftの方向性を知るのに参考になるだろうから、混ぜこぜにしている。

構文

SE-0345 if let shorthand for shadowing an existing optional variable

if letでoptional unwrappingするときにif let a = a {}if let a {}と書けるようにして、冗長さをなくす。

SE-0359 Build-Time Constant Values

コンパイル時に決まる(動的ではない)値の概念を導入する。そのような値は、文字列や数値、真偽値、associated valueのないenum、それらのArray、Dictionary、タプルに限られる。@const属性を導入し、プロパティや関数の引数に付ける。そのようなプロパティはコンパイル時に決まる値で初期化されていなければならないし、関数の引数もコンパイル時に決まる値でないといけない。これらの制約(コンパイラから見れば保証)を導入することで、コンパイル時にできる処理が増える。

標準ライブラリ

SE-0329 Clock, Instant, and Duration

(Foundationではなく)標準ライブラリに時刻に関する型を加える。非同期処理のようなタスクのスケジューリングに使う。

SE-0358 Primary Associated Types in the Standard Library

「SE-0346 Lightweight same-type requirements for primary associated types」で導入されたprimary associated typesを標準ライブラリでも設定する。

正規表現

SE-0348 buildPartialBlock for result builders

(「SE-0351 Regex builder DSL」を見越して)result buildersにフックとなるメソッドを追加して、挙動をカスタマイズしやすくする。このフックによってオーバーロードを減らせる。

SE-0350 Regex Type and Overview

正規表現型の導入。キャプチャされた値の型を型パラメータで表現するところがSwiftらしい。

SE-0351 Regex builder DSL

Result buildersでDSLを作り、正規表現を組み立てられるようにする。おもしろい。単におもしろいだけでなく、キャプチャした値を何らかの型に変換する処理も組み込める。

SE-0354 Regex Literals

正規表現リテラルを言語に組み込む。リテラルコンパイル時に評価され、正規表現として妥当であることが保証され、キャプチャの型も決まる。DSLにも組み込める。Swift 6以降は/のみで使えるが、Swift 5.7の時点では演算子の互換性の都合から#/を使う。

SE-0355 Regex Syntax and Run-time Construction

実行時に文字列から正規表現を作る。

SE-0357 Regex-powered string processing algorithms

StringCollection正規表現を活用するメソッドを追加。パターンマッチの追加。

非同期処理関連

SE-0302 Sendable and @Sendable closures

Sendableの概念自体はSwift 5.5からあるが、Sendableのチェックに関する警告は有効になっていなかった。Swift 5.6で有効にされそうだったが、あまりにも警告が多くなりすぎることから、結局無効にされた( https://github.com/apple/swift/pull/41368 )。Swift 5.7でついに有効になった。ついでに-warn-concurrencyオプションが-strict-concurrency=(minimal|targeted|complete)になっている( https://github.com/apple/swift/pull/42523 )。

SE-0338 Clarify the Execution of Non-Actor-Isolated Async Functions

Actorのasync関数から、他のactorに紐付かないasync関数を呼び出すとき、これまではactor上で実行されてしまっていた。これをやめて、actorに紐付かないasync関数は常にグローバルに共有されたプールで実行されることになる。これに伴ってSendableのチェックも行われる。

SE-0340 Unavailable From Async Attribute

Async関数はawaitの前後で実行されるスレッドが同一とは限らない。このような環境で呼び出されることを意図してない(単一のスレッドから呼び出されることを意図している)関数について、async関数からの呼び出しを防ぐように宣言できる。

SE-0343 Concurrency in Top-level Code

トップレベルでawaitできる。このときグローバル変数は暗黙的に@MainActorが関連付けられる。

分散actor

Actor間のやりとりを拡張していくと、(やりとりするメッセージがシリアライズできるなら)個々のactorが同一のプロセス、あるいは物理マシンに存在しなくてもいいんじゃないか、となっていく。しかしこれらが実際にどういうものになるのか、門外漢すぎてわかっていない。

SE-0336 Distributed Actor Isolation

SE-0344 Distributed Actor Runtime

型システム関連

SE-0309 Unlock existentials for all protocols

Associated typeやSelfを持つprotocolでもany Pとしてexistential typeのように扱える。これまではジェネリクスの制約として使うしかなかった。anyという表現はSwift 5.6の「SE-0335 Introduce existential any」ですでに実装されている。

SE-0326 Enable multi-statement closure parameter/result type inference

式が2つ以上のクロージャで、引数や返り値の型推論を有効にする。これまでも式が1つなら型推論されていたが、型チェッカーのパフォーマンスの問題で式が2つ以上のときは型を明示する必要があった。実装の改善でパフォーマンスの問題を解消できたため、2つ以上でも型推論を有効にさせられる。

SE-0328 Structural opaque result types

Opaque result typeは、protocolを返り値の型としてsome Pのように書けるもので、Swift 5.1から導入されている。これを拡張して、(some P)?(some A, some B)() -> some Pのようなパターンも書けるようにする。

SE-0341 Opaque Parameter Declarations

Opaque typeのsome Pを引数の宣言でも使えるようにする。これまでは型パラメータを制約する形で表現していた。

SE-0346 Lightweight same-type requirements for primary associated types

Protocolが1つ以上のassociated typeを持つとき、その一部をprimary associated typeであると宣言できる。Primary associated typeはProtocol<AssociatedType>というジェネリクスと同様の構文で実際の型を指定できる。これによって表記が簡単になる。

SE-0347 Type inference from default expressions

関数の引数の型にジェネリクスで型パラメータを使っていても、型推論によってデフォルト引数を持てるようにする。

SE-0352 Implicitly Opened Existentials

あるprotocol Pについて、any Psome Pとして渡せるようにする。

SE-0353 Constrained Existential Types

「SE-0346 Lightweight same-type requirements for primary associated types」でprimary associated typesが設定されたprotocolがexistential typeとして使える。

SE-0360 Opaque result types with limited availability

Opaque result typeを使っていて関数の返り値がsome Pのとき、その関数が実際に返す具体的な型Tは常に一致している必要がある。例えば関数の中で条件分岐して、ある場合はA、ある場合はBを返したりはできない。これを緩和して、#available()による条件分岐に限って型が違ってもいい、とする。

Swift Package Manager

SE-0339 Module Aliasing For Disambiguation

利用するモジュールに別名を与えて紛らわしくなくできる。

SE-0356 Swift Snippets

Swift Packageにスニペットを追加できる。

Swift 5.7の傾向

こうしてプロポーザルを概観すると、大まかな傾向が掴めるはずだ。

非同期処理関連の改善がいくつか見られる。データ競合を引き起こさない安全な平行処理のために、そしてSwift 6で完全に近い保護を達成するために準備が進められている。

わかりやすいのは正規表現に関するプロポーザルが多いことで、一連のプロポーザルによって、正規表現はSwiftの一級市民になる。Swiftでは何年も前にString Manifestoというのが議論されていて、このときに(正規表現ではないが)文字列のパターンマッチングが俎上に載せられていた。今回の正規表現について、これの一環とも思える。

もうひとつ、型システムについて大幅な改善がある。Opaque result typeのsomeやexistential typeのany、あるいはprimary associated typeによって、protocolの使い勝手が(あるいは書き味が)かなりよくなる。これらの一部はGenerics Manifestoで議論されていたが、ついに大きく手が入った格好である。

Swiftの進化による期待

ここで手が入った機能の多くは、例えばSwiftUIに大きな影響を与えそうだ。SwiftUIはSwiftの言語機能を活かしているし、特に型システム周りでは恩恵が大きそうに思う。

勝手な期待では、SwiftUIのナビゲーションをもっと抽象的に、あるいは宣言的に扱いたい。そういう機能が追加されるとして、アプリ内の任意の画面にディープリンクを設定するなら、正規表現の言語的サポートも大いに役立ちそうだ。

加えて、Swiftの言語的な進化による、Swiftのためのライブラリやフレームワークの進歩も期待している。Apple製のフレームワークのうち新しいものには、Swiftからのみ利用できるものが出てきている。それらはSwiftの言語機能を活かしていて、堅牢で使いやすい。今後もそのような、例えばasync/awaitやactorをうまく活用したフレームワークが増えていくだろうことに非常に期待している。例えばデータの永続化について、Swiftyなものが出てきてほしい。

ということで、今年もWWDCを楽しみにしています。