ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swiftの最新情報
Swiftの最新情報を紹介します。パフォーマンスの向上について解説します。より安全で拡張可能なSwiftパッケージを詳しく紹介しますので、Swiftの同時実行性がどれほど進歩したかご覧ください。また、Swift Regex、優れたジェネリクス、言語に組み込まれた他のツールも紹介します。これで、より柔軟で表現力の高いコードを作成できるようになります。
リソース
- Celebrating learning experiences from the 2021 Swift Mentorship Program
- Contribute to Swift
- Diversity in Swift
- Swift Mentorship Program
関連ビデオ
WWDC22
-
ダウンロード
♪ 落ち着いた雰囲気のヒップホップ音楽 ♪ ♪ こんにちは私はAngelaです 私はBeccaです 『Swiftの最新情報』へようこそ 本日はSwift 5.7.のすばらしい新機能を すべて紹介できることにワクワクしています 本日お話しする多くの内容は デベロッパの仕事を楽にするという Swiftの目標を立証します 皆さんがワークフローを カスタマイズするための 新しいツールをご紹介し すばらしい内部改善をいくつかご紹介します 次にSwiftの並行処理モデルの最新情報と 完全なスレッド安全を含む Swift 6への道をご紹介します そして 最後により明確でシンプルになった ジェネリクスを含むSwiftでの読み書きを 容易にする言語の改善についてと 新しい協力な文字列 処理機能についてお話します 話を始めるまえにSwiftが実に特別である 理由の1つについてお話ししましょう それは皆さんです Swiftが急速に成長できたのは 皆さんのご意見や貢献のおかげです コミュニティは Swiftに とって欠かせない存在です ドキュメント生成ツールDocC が去年発表され Swift.orgウェブサイトが オープンソースになって以来 今年 Swift ではより多くのプロジェクトが コミュニティで利用可能になりました オープンソースは活動的なコミュニティにより 導かれることで最大の力を発揮します Swift on Serverではワークグループモデルと Diversity in Swiftを使用し 特定の分野に興味を持つ コミュニティメンバーに 管理とサポートを提供してきました これが良好に進んでおり 新しいグループを2つ開始しました 1つはSwiftウェブサイトで反復するもので コミュニティのリソース的な存在となり もう1つはC++ 相互運用性向けで C++とSwift間のモデルの設計を形成します 新しい領域に入ることによりコミュニティの メンバーからのサポートが必要になります その一部としてDiversity in Swiftワークグループは 去年 Swiftメンタープログラムを開始しました このプログラムは 何から始めていいかわらない人や 特定の領域で専門知識を深めたい人のために すべてのワークグループに 役立つ進路を提供します 去年のプログラムは大成功を収めました 興味を持ったメンティがたくさんいました その結果 41のペアを作ることができました 去年の成功により今年もこのプログラムが 実施されることになったのです 興味を持つ方全員に 参加していただくには皆さんのような 今これをご覧の 積極的で経験豊富な 幅広い知識を提供し新しいつながりを作りたい デベロッパの協力が必要です メンタープログラムで 重要なのはコードだけでなく コミュニティ内で関係を築くことも重要です また 少しのガイダンスが 永続的な効果につながります それを確信するのは私だけではありません 去年アムリットはこのプログラムに参加し コンパイラと言語設計に注力しました 最初は好奇心から参加したプログラムが 目に見える貢献に変わりました 新しい領域に入るのは簡単ではありませんが それでも結果的に成功をおさめ さらに貢献するよう刺激を受けました 他の多くの人と同様に この体験でアムリは多くの機会を得ました コンパイラと言語設計に加え 去年 技術文書の作成やテストに始まり 投稿からSwift パッケージまで 広範囲の焦点領域が利用可能でした 今年は領域がさらに増えました 新しいトピックに関する機会も常にあります 興味のある領域がこの画面にない場合 申請書にそれを記入することができます さらに今年のプログラムでは 参加したいけれども能力がまだ低い人のために スターターバグ投稿用に 1年間のメンタリングを提供します 申請に興味がある場合や 詳細を知りたい場合は 最新の Swift ブログ投稿をご覧ください 注目のメンティーからの 詳細な考察を確認できます メンタープログラムはDiversity in Swiftが 提供するイニシアチブのほんの一部です メンタープログラムの詳細や その他のDiversity in Swift 取り組みについては Swift.org/diversityをご覧ください 扉をより開放的にするために 皆さんがお持ちのリソースで Swiftを使いやすくしました Linuxパッケージ形式へのサポートを追加し Swiftツールチェーン配信プロセスを Linuxプラットフォーム向けに合理化しました 新しいネイティブ ツールチェインインストーラでは Amazon Linux 2のRPMとCentOS 7が Swift.orgから直接ダウンロードできます このツールチェーンは実験的なものですので Swift.orgフォーラムで感想をお聞かせください Swiftは 基本的にAppの構築に使用されます しかしSwiftは 高レベルの スクリプトの使用から ベアメタル環境まであらゆるものを使用して スケーラブルにすることが構想されてきました これまでに使用されていない方法で Swiftが使用されるのを奨励するために 今年は Swiftにいくつかの 大きな変更が行われました スタンドアローン静的リンクバイナリ用に 標準ライブラリを縮小し 外部のUnicodeサポートライブラリへの 依存を中止し より迅速なネイティブ実装に置き換えました より小型で迅速なバイナリは イベント駆動型サーバ ソリューションで実行された 場合に利点となります Linuxではデフォルトで 静的リンクが装備されており サーバのコンテナ化された 展開のサポートが向上します このサイズ縮小によりSwiftはより制限された 環境にも適合するようになり AppleのSecure Enclaveでも 使用できるようになります SwiftはAppやサーバーから 制限付きプロセッサで役に立ちます これをすべて結びつけるのが パッケージエコシステムです Swift パッケージの今年の新機能は 皆さんの生活を楽にします まずSwift Package Manager TOFUが発表されました 食べ物ではありません TOFUはTrust On First Useの頭字語です 新しいセキュリティプロトコルで パッケージが 最初にダウンロードされた時に パッケージの指紋が記録されます その後のダウンロードではこの指紋が検証され 指紋が違う場合はエラーが発生します これは信頼とセキュリティが パッケージのエコシステムの 中心に構築されており自信を 持って使用できることを 証明できる例の1つです コマンドプラグインはSwiftデベロッパが ワークフローを改善するのに最適です これは拡張可能で安全なビルドツールを 提供するための最初のステップです この機能はドキュメント生成や ソースコード再編成などに使用できます シェルスクリプトで自動化を 書き 異なるワークフローを 維持する代わりにSwiftを使えます オープンソースフォーマッタや リンタを考えてください すべてのオープンソースツールは Xcodeと Swift Package Managerでご利用いただけます コマンドプラグインはオープンソースツールと Swift Package Managerの接着剤となります Swiftプロジェクトは自動ワークフローで シームレスな統合を提供するために オープンソースコミュニティの デベロッパツールを奨励します docC はソースコードで ドキュメントを統合するのに すばらしいツールです 今年は Objective-C と C サポートでさらに向上しました DocCでプラグインを 作成する方法を見てみましょう プラグインはシンプルなSwift コードです プラグインを定義するにはCommandPluginに プロトコルに同調するStrutを作成します 次に どのツールを呼び出すかを伝える 関数を追加するだけです この関数内でdocCを呼び出します プラグインを定義したら Swift PM コマンドライン インターフェースを介して メニューエントリとして Xcodeで利用可能になります これでSwift PMがドキュメントを作成し アクションをdocC実行ファイルに パスすることがわかります まだ続きます ビルドツールプラグインと いう 別のプラグインがあり このプラグインはパッケージでビルド中に 追加のステップを導入します ビルドツールプラグインを導入すると ビルドシステムのコマンドが生成され サンドボックスで実行されます いつでも直接実行ファイルで パッケージのファイルを変更するために 明確な許可を受けることができる コマンドプラグインとは異なります ビルドツールプラグインはソースコード生成か 特別な型のファイルの カスタム処理で使用できます これがビルドツールプラグインの パッケージレイアウトです この例ではplugin.Swiftは パッケージプラグインターゲットを実装する Swiftスクリプトです プラグインはSwift実行ファイルとして対処され 他のSwift実行ファイルと同じように プラグインを書きます どの実行ファイルコマンドを 実行し その結果どの出力が 期待されるかをビルドシステムに伝える 一連のビルドコマンドを定義して プラグインを実装できます パッケージプラグインは セキュアなソリューションで パッケージに拡張可能性を提供します プラグインの仕組みと自身のプラグインの 実装方法については2つのセッションを ご覧ください 『Swift Packageプラグインの紹介』 『Swift Packageプラグインを作成する』 パッケージの使用方法が増大するとともに モジュールの衝突が起こるかもしれません それは2つの異なるパッケージが モジュールを同じ名前で定義すると起こります これを解決するために Swift 5.7はモジュール 曖昧性解除を導入しました これは モジュールが 定義されたパッケージの外で モジュールの名前を変更できる機能です ここにすばらしいAppがあります ログインモジュールを定義した 2つのパッケージを呼び出すと 衝突しました Stunning App用にこれを修正するには moduleAliasesキーワードをパッケージ マニフェストのdependencies セクションに追加します そうすることで名前が同じだった 2つのモジュールに別々の名前を 付けることができます Swift 5.7ではパフォーマンスも向上しました ビルド時間を見てみましょう 去年 Swift Driverを書き直したことについて お話ししました SwiftのSwiftソースコード コンパイルを調整するプログラムです 去年の再構築により ビルドを著しくスピードアップするための 重要な変更が行われました 今回ドライバは別の実行ファイルではなく Xcodeビルドシステムの 中からフレームワークとして 直接使用できるようになりました これでビルドシステムを使って ビルドを より詳細に調整でき 並列化などが可能になります すばやいビルドに興味がある方は 『Xcodeビルドでの並列化に関する解説』 セッションで詳細をご覧ください ビルドの速度を証明するために Swiftで書かれた頻繁に使用するツールの 構築時間の例をお見せしましょう 10-core iMacでの速度改善は 5~25%です 次は型検査の速度の改善についてです 今年ジェネリクスシステムの 主要な部分を再実装し 型検査のパフォーマンスを改善しました プロトコルや「where」句などから 関数シグネチャを計算する部分です 以前の実装では時間のメモリの使用量は 関連するプロトコルが 増えると急激に拡大しました たとえばここには調整システムを定義する 複雑な一連のプロトコルと多くの関連型で たくさんのジェネリック要件があります 以前このコードの型検査には 17秒かかりましたが Swift 5.7ではこの例に対して 1秒以下と著しく迅速になっています 実行時間においても同等に改善しています Swift 5.7以前でのApp起動のプロトコル検査は iOSで最大4秒かかりました Appを起動するたびにプロトコルが必要で プロトコルが追加されるたびに 起動時間がより長くなりました 現在ではキャッシュされます Appの書き方により また使用されるプロトコルの数により iOS 16でAppが起動される場合 起動時間が半分になります 『Appサイズとランタイム パフォーマンスの改善』では 自身のAppでこれらの改善を 活用する方法が説明されています では皆さんが待ちに待っていた 情報をお伝えしましょう 去年 私たちはactorsやasync/awaitと共に 新しい並列化モデルを発表しました これは皆さんのAppの並列アーキテクチャに 改革的な効果をもたらしました Async/awaitとactorsはcallbackや 手動の待ち行列管理よりも安全かつ簡単です 今年は最前部でのデータ競合安全性で モデルをさらに強化しました 並列化はAppのコードベースにとって 非常に基礎的で重要であるため この変更をiOS 13とmacOS Catalinaで 再デプロイできるようにしました 旧式のOSにデプロイするには Appは旧式のOS用にSwift 5.5 並列ランタイムのコピーを一括します これはABI Stability以前の オペレーティングシステムに Swiftを再デプロイするのと似ています 次に この新しいモデルを 新しい方向に導きました 言語機能とサポートパッケージを 導入しました まずデータ競合の回避について説明します その前に 少し戻ってお伝えすべきことは Swiftで最も重要な機能の1つが デフォルトのメモリ安全性だということです Swiftユーザは値を変更しながら読むなど 予測不可能な動作で 何かをすることはできません この例では 同じ配列のcountに一致する 配列にある数字を削除しました 最初の配列のcountは3なので 配列から3を削除します しかしそうするとcountは2になります では配列から3と2の両方を削除するのか 3だけを削除するのか? 答えはどちらでもありません Swiftは これが行われるのを防止します 変更中に配列のcountに アクセスするのは危険だからです 同類のことをしてスレッドの 安全を守るのが目標です 私たちはデフォルトで起こる 低レベルのデータ競合を 消去する言語を想定しました 言い換えると予期せぬ動作を起こす 並列バグを防止するという意味です 別の例をお見せしましょう 同じ数字の配列を使い0を配列に追加する バックグラウンドタスクを作成し 配列の最後の要素を削除します おっと でも最後の要素の削除は 0の追加の前に起こるのか後に起こるのか? ここでも答えはどちらでもありません actorなどでアクセスを同期せずに バックグラウンドタスクから 配列を変更するのは危険であるため Swiftはこれが行われるのを防止します Actorはデータ競合排除への 最初のビッグステップです 今年は最終目標に前進するために 並列化モデルを微調整しました それぞれの actor を並列化の海で 孤立している島だと考えてください しかし異なるスレッドが 各actorに保存されている 情報をクエリするとどうなるでしょうか? このメタファーはこちらのセッションで 説明されています 『Swiftの並行処理でデータ競合を排除する』 メモリ安全からデフォルトのスレッド安全 それがSwift 6の目標です それを実現するために まず去年の並列化モデルを 先程申し上げた新しい言語機能で 更新しました もう1つ まだお伝えしていない事は 潜在的なデータ競合を特定する オプトイン安全チェックです より厳密な並列検査を行いたい場合は ビルド設定で有効にできます ではもう一度actorを見てみましょう このactorの概念を 分散actorで掘り下げてみましょう 分散actorはネットワークを境に 島を別のマシンに配置します この新しい言語機能は分散システムの開発を よりシンプルにしました ゲームAppを作成したいとしましょう Swiftならバックエンドを簡単に書けます ここでは分散actorはactorのようですが 異なるマシンにいる場合があります この例では ユーザとの ゲーム中に状態を維持する コンピュータプレイヤに目を向けています 分散キーワードはリモートマシンにいる actorを呼び出す必要があると期待する 関数に追加することもできます endOfRoundという関数も追加しましょう プレイヤごとに繰り返され makeMoveを呼び出します ローカルとリモートのプレイヤがいますが 誰が誰かを気にする 必要はありません 通常のactor呼び出しと違うのは 分散actorの呼び出しは ネットワークエラーにより 失敗する可能性があるところです ネットワークの障害が起こった場合 actorメソッドはエラーを出すので try キーワードの追加と actor外で関数を呼び出した時のために await キーワードの追加が必要になります コア言語のプリミティブを構築することで Swiftでサーバーサイドのクラスタ化された 分散システムの構築に 集中したオープンソースの Distributed Actorsパッケージも構築しました このパッケージにはSwiftNIOを使用した 統合されたネットワーク層 が含まれ クラスタの状態を 管理するSWIM consensus protocolを実装しました 『Swiftの分散アクタの紹介』では これらの新しい機能で分散システムを 構築する方法が説明されています また AsyncSequenceに対処する場合の一般的な 操作ですぐに使える ソリューションを提供するために Swift 5.5.でリリースされた 新しいオープンソースの 一連のアルゴリズムを発表しました これらのAPIをパッケージとして提供することで デベロッパはプラットフォームや OSバージョンのデプロイで柔軟性が持てます 複数の非同期シーケンスを組み合わせ 値を コレクションにグループ化する 方法がいくつかあります これはパッケージに含まれている いくつかのアルゴリズムです 『Swift Async Algorithmsについて』で この強力なAPIの使用方法を確認できます しかし 並列化にはパフォーマンスという 別の側面があります 今年はactorの優先度付けで actorsは 優先度が最も高い作業から実行でき オペレーティングシステムスケジューラで ディープインテグレーションを 続行できるようになりました モデルには優先順位の逆転 防止が内蔵されているので 重要度の低い作業が高い作業を 阻止することはできません 以前はAppで 並列のパフォーマンスの影響を 視覚化するのは困難でしたが それを行うためのすばらしい ツールが開発されました Instruments の Swift Concurrencyビューは パフォーマンスの問題の調査に役立ちます Swift TaskとActors instrumentsは 並列コードを視覚化最適化するための ツール一式を提供します Swift Tasks Instrument上部には 同時に実行されているタスクの数や その時点までに作成されたタスクの合計数など 有益な統計を提供します このウィンドウの下半分では Task Forestと呼ばれるものがあります これは構造化並列コードの タスク間の親子関係を 図形表現したものです これはSwift Actor Instrumenttの 詳細なビューの1つです この期待の新しいツールを使用する方法は 『Swiftの並行処理を視覚化して最適化する』 をご覧ください また 新しいパッケージの使用もお忘れなく そしてフォーラムで感想をお聞かせください では次はBeccaがSwift言語の ユーザビリティの改善についてお話しします 言語はツールですそしてこのツールの 面白い点は構築するものに 影響を与えるところです ハンマーしかない場合に 何かを構築する場合は ネジでなくクギを使います ツールが一式揃っていたとしても ハンマーの柄が握りやすく スクリュードライバーは握りにくい場合 クギを使うことを選択するかもしれません 言語も同じです 何かを表現するための適した ツールが Swiftにあれば より頻繁に使用するでしょう 今年 コードに実行してもらいたい作業を 表現するためのツールが 多くの方法で改善されました これらの変更の一部は頻繁に行うことに対する 便利性のためのものです 例えば Swift では等号の両側の名前が同じなら if letを使用するのは一般的です 結局のところラップされていない値には オプションのものに付けた名前より 良い名前はないでしょう しかし名前が長い場合 その反復が面倒になってきます 名前を省略したくなりますが そうするとコードが不可解になります 後にオプション変数の名前を変更しても 省略が同期しなくなります Swift 5.7ではこの一般的なパターン向けに 新しい省略表現を導入しました オプションのラップをほどき ラップされていない値に 同じ名前を付ける場合は 右側を削除すればいいだけです Swiftは同じだと推測します guardでも同じ効果があります even whileでも同じです 小さな変更を加えた時に 機能が動作しなくなる点にも 目を向けました 例えば Swiftは単一のステートメント クロージャ内に書かれたコードに基づいて どの呼び出しを返すか常に考えだします このcompactMap呼び出しでは クロージャはparseLineの 値を返しparseLine関数は MailmapEntryを返すのでSwiftはエントリが MailmapEntryの配列であるべきだと解明します これで複数のステートメント 制御フロー機能など より複雑なクロージャでも機能するようになり do-catchやif...elseを使用したり クロージャの結果型を手動で指定することなく print呼び出しを追加したりできます また 実際の危険を警告しない 危険フラグにも目を向けました Swiftは型とメモリの安全を懸念しています 皆さんがミスをしないように ポインタを異なるポインタ型に 自動的に変換することや 生ポインタを型ポインタに 変換することはしません これは特定の変換を許可する Cとは非常に異なります 例えば ポインタが指す値の符号属性の変更や Cのポインタ規則を破ることなく ポインタを char *に型変換しバイトとしてアクセスします しかしC APIがSwiftにインポートされた場合 ポインタの動作が問題を起こす場合があります 元のデベロッパがわずかに不一致した APIを設計しCでは自動変換が処理され Swiftではエラーになります Swiftで1つのポインタを異なる型のように アクセスするのは非常に危険です そのため動作を明確に説明する必要があります しかし ポインタを直接Cにパスするなら それは無意味ですなぜならCでは ポインタの不一致は完全に合法だからです ですのでこのケースでは 率直に危険であることにして 対処しました これが重要なのは Swiftは 安全性を重要視しますが C系のコードも大切にするからです だから CとObjective-C 相互運用性は非常に充実し シームレスなのです また Angelaの先述のように 同等な機能を持つ C++ 相互運用性を構築するための C++ワーキンググループが できたのもそのためです このようなC関数を使って 不必要な面倒を起こしたくありません そこで Swiftにはインポートされた機能や メソッドに対する 別の 規則セットが作成されました 通常SwiftではできないCでは合法な ポインタ変換を可能にします これでSwiftコードをAPIで シームレスに使用できます ここまでは皆さんがすでにお持ちのツールの 小さな改善についてお話ししました しかし今年は Swift文字列から情報を 抽出するための真新しい ツールが作成されました これは文字列から情報を解析する 関数です この類のタスクは Swiftでは常に挑戦的でした 望むものを得るまで検索 分割 スライスを 何度も繰り返す必要がありました 人はこれに気づいた場合 文字列のインデックスを操作するには いかに言葉が冗長するかと 小さなことに気を配ります しかしそうなると全体像を見逃しがちです なぜならこの構文を変えたとしても このコードを見た時に質問している 基本的な答えが出ませんここにパスされる 行変数は実際にどんなものであるか? どんな文字列を分解しようとしているのか? 長く見つめていると 簡素化されたmailmapを解析し gitリポジトリに保管したファイルで デベロッパの名前を修正しようとしてます しかしその情報の抽出に検索とスライスが 深く関与しており理解することができません 文字列のスライスで混乱し どの文字列だったかを思い出すのに苦労します 問題はこの2つの表現ではなく 全体にあります これを全部取り除き より優れたものに置き換える必要があります 別のアプローチが必要です 1つはコードが一致させたい文字列の 絵を描き 言語がその方法を考え出します 命令的ではなく宣言的なアプローチです Swift 5.7では 正規表現を 書くことでそれが実現します 正規表現は文字列の パターンを説明する方法です 50年以上 言語とツールは デベロッパが高密度で情報が詰まった構文を 書くことを許可してきました 一部の皆さんはすでにFoundationの NSRegularExpressionクラスの grepなどの コマンドラインのXcode検索バーや 他のプログラミング言語でご使用でしょう その構文がSwiftの正規表現 リテラルで利用可能になり 他のデベロッパツールと同様に機能します しかしまだ正規表現を使用したことがなく 「それって本物のコード?」 と思う方もいらっしゃるでしょう 無理もありません これはシンボルやニーモニックで書かれており 読むためにはこれらを記憶する必要があります この言語を知っている方にとって デベロッパの名前に一致するなど この正規表現がすごいと感じる部分は いくつかの単純な一致条件が 組み合わされただけです しかし11文字に詰め込むには かなりの数の動作です 非常に圧縮されており 経験豊富なデベロッパでさえ 複雑なものを理解するのに 時間を要することがあります しかしシンボルの代わりに言葉で 同じ種類の一致条件を書けるなら? 理解しやすくなりそうですね 実際にこれを1つにまとめると SwiftUIにかなり似ています 正規表現リテラルのすばらしい代用となります なのでSwiftがこれをサポート するのはすばらしいことです RegexBuilderライブラリは正規表現のための 新しいSwiftUI式の言語を提供し 従来の 構文よりも使いやすく読みやすくなっています 正規表現リテラルと同じ機能を果たしますが 記憶が必要なシンボルや省略表現の代わりに 理解や検索可能な言葉で 動作を説明します Regex ビルダは初心者に最適ですが この機能は初心者専用ではありません 正規表現リテラルの通常の機能以上の 能力があります まず 正規表現を再利用可能な 正規表現コンポーネントに 変えることができます SwiftUIビュー階層をビューに変えるのと 同じです Builder構文で作成された別の正規表現からの コンポーネントを使い 再帰的にすることもできます 正規表現ビルダは一部のSwift型を 正規表現に直接入れることもサポートします 例えば 文字列リテラルは 中のテキストと完全に一致し 特別なエスケープ処理は必要ありません 正規表現リテラルを 正規表現ビルダの途中で使用することもでき 正規表現ビルダの明確さと リテラルの簡潔さの間でバランスを保てます 他にもこのFoundation 日付形式スタイルのように 正規表現ビルダでカスタム解析 ロジックを統合でき データを取得前にリッチ型に変換できます 最後に どの構文を使っても 正規表現は役に立つ多くの 一致メソッドをサポートし 使いやすい型付きキャプチャをサポートします 椅子に座りながら身もだえしている 正規表現のオタクに Swift Regexはほとんどの高度な正規表現実装に 匹敵する機能セットが付いた 最新のオープンソース マッチングエンジンを使います リテラル構文は Unicode正規表現標準に対応し 卓越したレベルのUnicode正確性を備えています 例えば「点」はデフォルトで すべての文字に一致し Unicode.ScalarやUTF-8バイトではありません Swift Regexを使用する場合AppはmacOS 13 またはiOS 16とSwift Regexエンジン内蔵の OSで実行される必要があります Swift Regexが全体の言語です 実際には2つの言語です なので他にもお伝えすることはあります 『Swift Regexの紹介』 『Swift Regex: 上級編』では 使い方の詳細が説明されています 最後は私たちが保有する ツールを全体的に見て 改善するために多くの 変更を行った部分があります それはジェネリクスとプロトコルです これがどのように改善されたかを説明するには プロトコルの例が必要です gitクライアントを書いており mailmapを2つの異なる方法で 表現する必要があります commitを表示すると ディクショナリで型を使用し 名前をすばやく検索します しかしユーザがmailmapを編集する場合 配列で型を使用し入力を元の順序で 保ちます Mailmapというプロトコルがあり どちらもそれに従うため mailmapパーサはエントリ追加ができますが パーサがMailmapプロトコルに使用できる 方法は2つあります このaddEntrieの2つの 異なる関数で説明するつもりでしたが この2つがどう異なるかを 説明するのは困難です なぜならSwiftは 同じ 構文を使っているからです ここでは「Mailmap」の意味が こことはわずかに異なっていたのです
継承リストジェネリックパラメータリスト ジェネリックコンフォーマンスリスト 不透明結果型でプロトコルに名前を付けると 「このプロトコルに従うインスタンス」となり 変数型ジェネリック引数 ジェネリック同型制約 関数パラメータまたは結果型では 「このプロトコルに従うインスタンスを含む ボックス」という意味になります この区別は重要ですなぜならこのボックスは 通常場所を多く取り操作に時間がかかり 中には インスタンスのすべての機能が ないからです しかしこのボックスを使用する場所は 使っていない場所と見た目は一緒です ですので使用しているかどうかはわかりづらく Swift 5.7 はこの見過ごしを修正します 適合型を含むボックスの いずれかを使用する場合 Swiftは 皆さんがanyを 入力することを期待します これはSwift 5.7以前では 必須ではありませんでしたが これを行うことが推奨されており 明確に書かなかったとしても インターフェースや エラーメッセージで表示されるようになります 右側の列にある内容を書くために 最適な方法はanyを使用することです そうすればボックスを 使用しているとわかるようになります この例では anyがパラメータの1つに 記入されているためこの2つの関数の 違いがわかりやすくなりました addEntries1はMailmapをジェネリック型 addEntries2はany型として受け取ります また any 型の制限に及んだ場合に エラーメッセージで 状況を説明しやすくなります 例えばこのmergeMailmaps関数は Mailmapをジェネリック パラメータにパスしようとし Mailmapは自身に適合できないという エラーメッセージが表示されます 少し矛盾しているように見えますが 今はany型の概念があるので 何が起こっているかをより明確に説明できます 問題はany Mailmap mailmapを含んでいるボックスが Mailmapプロトコルに適合しない点です なのでそのボックスをパスしようとしても ジェネリックパラメータに適合しません 中のインスタンスをパスしようとすると どうにかしてボックスを開き 中のmailmapを出してパスしますが このようなシンプルなケースの場合 Swiftが皆さんの代わりにそれを行ってくれます ボックスを開き中のインスタンスを出し ジェネリックパラメータにパスしてくれます なのでこのエラーメッセージは あまり表示されなくなります anyよりもさらに嬉しい 改善があります 以前プロトコルがセルフ型や 関連型を使用した場合 any型として使用できませんでした Equatableのようにプロトコルに適合することも できませんでした しかしSwift 5.7ではこのエラーが… フ~ 消えました 多くのデベロッパがこれで苦労しているので ソースで修正できたことに満足しています Mailmapなどには十分な修正ですが 改善はまだ続きます Collectionのような 洗練されたプロトコルでさえ any型として使用できるようになりました 要素型を指定することもできます 新機能「primary associated types」のおかげです 多くのassociated typeは 基本的にただの実装詳細です Collectionがインデックス イテレータ サブシーケンスにどの型を 使うかは気にしません ただサポートされる型を使用するだけです しかし要素では話が違います Collectionがどの要素型を使うか常に 気にすることはありませんが 要素では制約や返還など 何かする必要があります ほぼ全員のプロトコルユーザが気にかける 要素のような型に関連する場合 プロトコル名の後にカギ括弧を使って それに名前を付けプライマリ関連型にします その後 プロトコルのプライマリ関連型を any Collectionを含む プロトコルの名前を書ける 場所ならどこにでもカギ括弧の構文で 制約することができます 一部の方はこの型を見てこう言うかもしれません 「待てよ」 「AnyCollectionというのが すでにあるじゃないか」 anyを大文字にして一緒に 実行するだけなのか?」 そのとおりです 古いAnyCollectionは型消去ラッパーで any型と同じ目的を果たす 手書きのstructです ここでの違いはAnyCollection structは 見たことがないくらい退屈で長い ボイラープレートのコードで any型は 組み込みの言語機能で 基本的な機能を 無料で果たします AnyCollection structは any型では完全に一致しない 機能がまだあるため 後方互換性のために残ります しかしコードに独自の型消去ラッパーが あるならボックス型やクロージャの代わりに 組み込みのany型で再実装するか 型エイリアスで置き換えてみてはどうでしょう Swiftでは any型が著しく向上されました anyキーワードが導入されたため どこで使用するか確認できます ジェネリック引数にパスすることもでき プロトコルでそれを使用する制限を 廃止しました さらにany型のプライマリ関連型を 制約できるようになりました しかしこれらのさまざまな改善が施されても any型にはまだ制限があります 例えばMailmapがEquatableに適合した場合 any Mailmapsを使用することができますが 等号演算子を一緒に使用することはできません 等号演算子はmailmapsが同じ 具象型が必要だからですしかしany Mailmapを 2つ使用するとそれは保証されません ですのでany型はかなり改善されましたが 機能とパフォーマンスの面で まだ重要な制限があります なので多くの面で使用しない方がいいでしょう 代わりにジェネリクスを使用してください addEntriesの2つのバージョンに戻り その知恵を適用してみましょう どちらのバージョンも まったく同じ動作をしますが 上のものはジェネリック型を使用し 下はany型を使用します ジェネリックバージョンはより効率的で 機能性が高いためそちらを使用します any型は読み書きがより簡単なため そちらを使いたくなるでしょう ジェネリックバージョンを書くには 2つのジェネリック型の名前を宣言し どちらも制約し 最後に パラメータ型としてこの名前を使用します 「any Collection」や「any Mailmap」と 書くよりも面倒です なので欠点があってもanyを使いたくなります しかしそれはハンマーの柄は握りやすいため スクリュードライバの代わりにハンマーを 使うという私の先ほどの話と同じになります その選択をする必要がないべきです そこでSwiftはジェネリックを any型と同じくらい 使いやすくしました ジェネリックパラメータが 使われているのが1ヶ所なら 省略語としてキーワードを書くことができます プライマリ関連型もサポートされており すべてのmailmapエントリのコレクションを 理解しやすいコードで承認することができます それがツールボックスにあるなら ジェネリクスを避ける必要はもうありません ジェネリクスとany型で選択肢があるなら ジェネリクスも同様に使いやすくなります 「any」の代わりに「some」と書くだけです この際作業に最適なツールを使うべきです プロトコルとジェネリクスについては 大まかに説明しただけです 詳細や Swiftのジェネリック機能の おさらいに関しては 今年はもう2つトークがあります 『Swiftのジェネリクスを活用する』と 『Swiftでプロトコル インターフェイスを設計する』
Angelaと私は20件以上の Swiftの変更についてご紹介しました しかしまだまだ他にも変更はあります すべての変更はピッチ提案 評価され SwiftフォーラムのEvolution委員会で 公的に承認されました これらすべてが形成され実現したのは Apple外部のコミュニティメンバーのおかげです 皆さんもメンバーの一員なら Swift 5.7を すばらしい リリースにしてくれてありがとうございました 次の変更に貢献したい場合は Swift.org/contributingで 参加方法をご覧ください ありがとうございました ハッピーコーディング!
♪
-
-
7:19 - Command plugins
@main struct MyPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) throws { let process = try Process.run(doccExec, arguments: doccArgs) process.waitUntilExit() } }
-
8:34 - Build tool plugins
import PackagePlugin @main struct MyCoolPlugin: BuildToolPlugin { func createBuildCommands(context: TargetBuildContext) throws -> [Command] { // Run some command } }
-
8:39 - Implementing a build tool plugin
import PackagePlugin @main struct MyCoolPlugin: BuildToolPlugin { func createBuildCommands(context: TargetBuildContext) throws -> [Command] { let generatedSources = context.pluginWorkDirectory.appending("GeneratedSources") return [ .buildCommand( displayName: "Running MyTool", executable: try context.tool(named: "mycooltool").path, arguments: ["create"], outputFilesDirectory: generatedSources) ] } }
-
9:23 - Module disambiguation with module aliases
let package = Package( name: "MyStunningApp", dependencies: [ .package(url: "https://.../swift-metrics.git"), .package(url: "https://.../swift-log.git") ], products: [ .executable(name: "MyStunningApp", targets: ["MyStunningApp"]) ], targets: [ .executableTarget( name: "MyStunningApp", dependencies: [ .product(name: "Logging", package: "swift-log"), .product(name: "Metrics", package: "swift-metrics", moduleAliases: ["Logging": "MetricsLogging"]), ])])
-
9:42 - Distinguishing between modules with the same name
// MyStunningApp import Logging // from swift-log import MetricsLogging // from swift-metrics let swiftLogger = Logging.Logger() let metricsLogger = MetricsLogging.Logger()
-
11:09 - Example set of protocols
public protocol NonEmptyProtocol: Collection where Element == C.Element, Index == C.Index { associatedtype C: Collection } public protocol MultiPoint { associatedtype C: CoordinateSystem typealias P = Self.C.P associatedtype X: NonEmptyProtocol where X.C: NonEmptyProtocol, X.Element == Self.P } public protocol CoordinateSystem { associatedtype P: Point where Self.P.C == Self associatedtype S: Size where Self.S.C == Self associatedtype L: Line where Self.L.C == Self associatedtype B: BoundingBox where Self.B.C == Self } public protocol Line: MultiPoint {} public protocol Size { associatedtype C: CoordinateSystem where Self.C.S == Self } public protocol BoundingBox { associatedtype C: CoordinateSystem typealias P = Self.C.P typealias S = Self.C.S } public protocol Point { associatedtype C: CoordinateSystem where Self.C.P == Self }
-
13:14 - Memory safety in Swift
var numbers = [3, 2, 1] numbers.removeAll(where: { number in number == numbers.count })
-
14:10 - Thread safety in Swift
var numbers = [3, 2, 1] Task { numbers.append(0) } numbers.removeLast()
-
15:54 - A distributed actor player and a distributed function
distributed actor Player { var ai: PlayerBotAI? var gameState: GameState distributed func makeMove() -> GameMove { return ai.decideNextMove(given: &gameState) } }
-
16:20 - A distributed actor call
func endOfRound(players: [Player]) async throws { // Have each of the players make their move for player in players { let move = try await player.makeMove() } }
-
20:12 - Optional unwrapping
if let mailmapURL = mailmapURL { mailmapLines = try String(contentsOf: mailmapURL).split(separator: "\n") }
-
20:29 - Optional unwrapping with long variable names
if let workingDirectoryMailmapURL = workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n") }
-
20:35 - Cryptic abbreviated variable names
if let wdmu = workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: wdmu).split(separator: "\n") }
-
20:46 - Unwrapping optionals in Swift 5.7
if let workingDirectoryMailmapURL { mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n") } guard let workingDirectoryMailmapURL else { return } mailmapLines = try String(contentsOf: workingDirectoryMailmapURL).split(separator: "\n")
-
21:07 - Closure type inference
let entries = mailmapLines.compactMap { line in try? parseLine(line) } func parseLine(_ line: Substring) throws -> MailmapEntry { … }
-
21:33 - Type inference for complicated closures
let entries = mailmapLines.compactMap { line in do { return try parseLine(line) } catch { logger.warn("Mailmap error: \(error)") return nil } } func parseLine(_ line: Substring) throws -> MailmapEntry { … }
-
22:15 - Mismatches that are harmless in C...
// Mismatches that are harmless in C… int mailmap_get_size(mailmap_t *map); void mailmap_truncate(mailmap_t *map, unsigned *sizeInOut); void remove_duplicates(mailmap_t *map) { int size = mailmap_get_size(map); size -= move_duplicates_to_end(map); mailmap_truncate(map, &size); } // …cause problems in Swift. func removeDuplicates(from map: UnsafeMutablePointer<mailmap_t>) { var size = mailmap_get_size(map) size -= moveDuplicatesToEnd(map) mailmap_truncate(map, &size) }
-
22:33 - Better interoperability with C-family code
func removeDuplicates(from map: UnsafeMutablePointer<mailmap_t>) { var size = mailmap_get_size(map) size -= moveDuplicatesToEnd(map) withUnsafeMutablePointer(to: &size) { signedSizePtr in signedSizePtr.withMemoryRebound(to: UInt32.self, capacity: 1) { unsignedSizePtr in mailmap_truncate(map, unsignedSizePtr) } } }
-
23:41 - String parsing is hard
func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[activeLine.index(after: emailEnd)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[activeLine.index(after: nameEnd)..<emailEnd] return MailmapEntry(name: name, email: email) }
-
24:05 - String parsing is still hard with better indexing
func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[(emailEnd + 1)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[(nameEnd + 1)..<emailEnd] return MailmapEntry(name: name, email: email) }
-
24:20 - What's the problem?
let line = "Becca Royal-Gordon <beccarg@apple.com> # Comment" func parseLine(_ line: Substring) throws -> MailmapEntry { func trim(_ str: Substring) -> Substring { String(str).trimmingCharacters(in: .whitespacesAndNewlines)[...] } let activeLine = trim(line[..<(line.firstIndex(of: "#") ?? line.endIndex)]) guard let nameEnd = activeLine.firstIndex(of: "<"), let emailEnd = activeLine[nameEnd...].firstIndex(of: ">"), trim(activeLine[activeLine.index(after: emailEnd)...]).isEmpty else { throw MailmapError.badLine } let name = nameEnd == activeLine.startIndex ? nil : trim(activeLine[..<nameEnd]) let email = activeLine[activeLine.index(after: nameEnd)..<emailEnd] return MailmapEntry(name: name, email: email) }
-
24:55 - Drawing a picture
"Becca Royal-Gordon <beccarg@apple.com> # Comment" / space name space < email > space # or EOL / / \h* ( [^<#]+? )?? \h* < ( [^>#]+ ) > \h* (?: #|\Z) /
-
25:10 - Swift Regex using a literal
func parseLine(_ line: Substring) throws -> MailmapEntry { let regex = /\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/ guard let match = line.prefixMatch(of: regex) else { throw MailmapError.badLine } return MailmapEntry(name: match.1, email: match.2) }
-
25:46 - Did a cat walk across your keyboard?
/\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/
-
26:34 - Regex builder
import RegexBuilder let regex = Regex { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) ChoiceOf { "#" Anchor.endOfSubjectBeforeNewline } }
-
27:05 - Turn a regex into a reusable component
struct MailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) ChoiceOf { "#" Anchor.endOfSubjectBeforeNewline } } }
-
27:30 - Use regex literals within a builder
struct MailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) /#|\Z/ } }
-
27:39 - Use Date parsers within Regex builders
struct DatedMailmapLine: RegexComponent { @RegexComponentBuilder var regex: Regex<(Substring, Substring?, Substring, Date)> { ZeroOrMore(.horizontalWhitespace) Optionally { Capture(OneOrMore(.noneOf("<#"))) } .repetitionBehavior(.reluctant) ZeroOrMore(.horizontalWhitespace) "<" Capture(OneOrMore(.noneOf(">#"))) ">" ZeroOrMore(.horizontalWhitespace) Capture(.iso8601.year().month().day()) ZeroOrMore(.horizontalWhitespace) /#|\Z/ } }
-
27:49 - Matching methods and strongly type captures in Regex
func parseLine(_ line: Substring) throws -> MailmapEntry { let regex = /\h*([^<#]+?)??\h*<([^>#]+)>\h*(?:#|\Z)/ // or let regex = MailmapLine() guard let match = line.prefixMatch(of: regex) else { throw MailmapError.badLine } return MailmapEntry(name: match.1, email: match.2) }
-
29:02 - A use case for protocols
/// Used in the commit list UI struct HashedMailmap { var replacementNames: [String: String] = [:] } /// Used in the mailmap editor UI struct OrderedMailmap { var entries: [MailmapEntry] = [] } protocol Mailmap { mutating func addEntry(_ entry: MailmapEntry) } extension HashedMailmap: Mailmap { … } extension OrderedMailmap: Mailmap { … }
-
29:26 - Using the Mailmap protocol
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
31:05 - `Mailmap` and `any Mailmap`
func addEntries1<Map: Mailmap>(_ entries: Array<MailmapEntry>, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
31:17 - Improvements to `any` types
extension Mailmap { mutating func mergeEntries<Other: Mailmap>(from other: Other) { … } } func mergeMailmaps(_ a: any Mailmap, _ b: any Mailmap) -> any Mailmap { var copy = a copy.mergeEntries(from: b) return a }
-
32:21 - More improvements to `any` types
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func addEntries2(_ entries: Array<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
32:54 - Using Collection as an `any` type
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
33:04 - Primary associated types
protocol Collection<Element>: Sequence { associatedtype Index: Comparable associatedtype Iterator: IteratorProtocol<Element> associatedtype SubSequence: Collection<Element> where SubSequence.Index == Index, SubSequence.SubSequence == SubSequence associatedtype Element }
-
33:42 - Using primary associated types in Collection
func addEntries1<Entries: Collection<MailmapEntry>, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } } extension Collection<MailmapEntry> { … }
-
34:35 - Example of type erasing wrappers
struct AnySprocket: Sprocket { private class Base { … } private class Box<S: Sprocket>: Base { … } private var box: Base // …dozens of lines of code you hate // having to maintain… }
-
34:38 - Replace boxes with built-in `any` types
struct AnySprocket: Sprocket { private var box: any Sprocket // …fewer lines of code you hate // having to maintain… }
-
34:44 - Or try type aliases
typealias AnySprocket = any Sprocket
-
35:09 - `any` types have important limitations
protocol Mailmap: Equatable { mutating func addEntry(_ entry: MailmapEntry) } func areMailmapsIdentical(_ a: any Mailmap, _ b: any Mailmap) -> Bool { return a == b }
-
35:44 - Using generic types vs. `any` types
func addEntries1<Entries: Collection<MailmapEntry>, Map: Mailmap>(_ entries: Entries, to mailmap: inout Map) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
36:40 - `some Mailmap` and `any Mailmap`
func addEntries1<Entries: Collection<MailmapEntry>>(_ entries: Entries, to mailmap: inout some Mailmap) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
36:50 - `some Mailmap` and `any Mailmap` with Collection and primary associated types
func addEntries1(_ entries: some Collection<MailmapEntry>, to mailmap: inout some Mailmap) { for entry in entries { mailmap.addEntry(entry) } } func addEntries2(_ entries: any Collection<MailmapEntry>, to mailmap: inout any Mailmap) { for entry in entries { mailmap.addEntry(entry) } }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。