Swiftオープンソース化の衝撃

エンジニアの id:cockscomb です。この記事ははてなデベロッパーアドベントカレンダー2015の8日目の記事です。昨日は id:Songmu による Markdownドキュメントをgithubで管理して、はてなブログでホストする ~ Mackerelの場合 でした。


Swiftがオープンソース化されて数日が過ぎました。皆さんいかがお過ごしでしょうか。

Swiftのオープンソース化そのものは今夏のWWDCでアナウンスされていた通りです。しかし私を含めた多くのSwiftプログラマーは、このオープンソース化にとても興奮しています。

WWDC 2015のキーノートで、Swiftが今年中にオープンソースになり、OS Xに加えてLinux上での動作がサポートされると発表されたとき*1、私たちはそれを予感していたにも関わらず熱狂しました。しかし同時に、いったいどこまでがオープンソースになるのか、という疑問がわき上がります。Appleはこの時点で、Swiftのコンパイラと標準的なライブラリを公開する予定であると表現していました。

AppleプラットフォームのためのSwiftはDarwinやObjective-Cにいくらか依存しています。またSwiftの標準ライブラリと呼べるものは決して高機能ではなく、それだけではあまり多くのことができません。Appleプラットフォーム専用の種々のフレームワークが活用できる分には困りませんが、逆にそれらがなければ自分たちいろいろ用意しなくてはなりません。

実際にオープンソース化されたSwiftには、とても多くの機能が含まれていました。本記事ではオープンソース化されたSwiftの世界を一通り眺めて、Swiftを取り巻く環境について理解を深めていきます。

オープンソースになったSwift

コンパイラ

まずはSwiftコンパイラ。SwiftのコンパイラはLLVMのフロントエンドで、Clangと近い位置づけにあります。ソースコードをパースしてASTを構築し、型推論を含むセマンティックな解析を経て、Swift Intermediate Language (SIL)と呼ばれる中間言語を生成します。ここでさらに静的な解析を行ってプログラムに間違いがないかチェックし、そして最適化まで行います。SILには多くの情報が残されているため、高度な解析や最適化に適しています。これらの後にLLVM IR (Intermediate Representation)が生成されます。これがSwiftコンパイラの仕事です。この後にLLVM IRがLLVMによって最適化され、アーキテクチャ毎のバイナリに変換されるのです。SwiftコンパイラはLLVM IRを生成することで、後の工程をLLVMに任せることができます。またこの他にもSwiftコンパイラは、CやObjective-Cで書かれたコードを呼び出すために、Clangから必要な情報を収集するなどの機能も備えています。

f:id:cockscomb:20151208123744p:plain:w281:h456

Swiftコンパイラの役割*2

標準ライブラリ

Swift標準ライブラリは、基本的な文字列や数値などのデータ型や、辞書や配列などのコレクション型を提供します。またジェネリクスやキャスト、メモリ管理のような動的な機能を提供するランタイムを持ちます。標準ライブラリは言語仕様と密に結合しています。例えば様々なリテラルに対応するデータ型は標準ライブラリが持っており、あるいは標準ライブラリのOptional型にはそれを簡単に利用できる構文が言語仕様に含まれています。すなわち標準ライブラリと呼んでいるものは、言語仕様を実現するためのコンパイラ以外の部分であると言うことができます。

コアライブラリ

オープンソース化されたSwiftには標準ライブラリの他に、コアライブラリと呼ばれるものが準備されています。これらはSwift 3とともに正式に利用できるようになる予定で、FoundationlibdispatchXCTestの3つに分けられています。

Foundation

FoundationはAppleプラットフォーム用の同名のフレームワークをSwiftで書き直したものです。もともとのFoundationは、Core FoundationというCのインターフェースを持つフレームワークをObjective-Cでラッピングしたような構成でした。Swiftでも同じようにCore Foundationをラップするのを基本として、DarwinやObjective-Cランタイムへの依存を無くしたものになります *3Foundationによって、日時やURLといった様々なオブジェクトを表現できるクラス群や、HTTP通信などのネットワーク関連機能、あるいはスレッドを抽象化したクラスなども利用できるようになります。

新しいFoundationのAPIの命名規則は、いまのところは既存のものと同じ状態を保っています。しかし今後API Guidelinesに合わせて変わっていくことがアナウンスされており、よりSwiftらしいものへ置き換えられていくでしょう。例えばいわゆるNSプリフィックスがなくなって、NSObjectObjectになります。新しいFoundationの多くはまだ未実装で、今後の展開が気になります。

ところでもともとのFoundationはObjective-Cランタイムと連携して動的な機能を提供するレイヤーでもありました。このような機能は失われるでしょうが、しかしこれらはOS XやiOSのGUIアプリケーションアーキテクチャの根幹でもありました。いまのところAppleプラットフォーム向けにはこれまでのFoundationを使うようにアナウンスされていますが、将来的には統合される可能性があります。そのとき、Objective-Cランタイム由来の動的なシステムをSwiftで再現するのか、あるいはアーキテクチャを大きく変えてしまうのか、非常に興味を惹かれます。

libdispatch

libdispatchはGrand Central Dispatch (GDC)というマルチコアハードウェア向けに並行プログラミングを実現するライブラリです。やはりOS XやiOSに同名の機能が備わっています。クロージャを使って小さい単位で処理を記述し、これをGCDの管理するキューへ追加することでうまく並列実行させることができます。内部でハードウェアに最適化されたサイズのスレッドプールを管理していて、プライオリティなどに合わせて自動的に適切な並列度で実行されます。毎回スレッドを作るといったオーバーヘッドを減らしながら、並行プログラミングを可能な限り簡単に、高いパフォーマンスで実行するための仕組みです。

XCTest

XCTestは単体テストのためのフレームワークです。Xcodeに同名のテストフレームワークが備わっていますが、これと同じインターフェースを実現しています。ただし、もともとは動的にテストケースのクラスやメソッドを見つけていましたが、これらはObjective-Cランタイムに依存していました。これを排するために、テストケースはすべて人間がリストアップしてやる必要があります。XCTMain関数にテストケースクラスのインスタンスを渡したり、個々のテストケースクラスにvar allTests : [(String, () -> ())] { get }というcomputed propertyを実装して、テストメソッドをすべて列挙しなければなりません。

コアライブラリの公開は明らかに期待を上回るものでした。これだけのものがあれば、Linux上でも高度なプログラミングが簡単に実現できることでしょう。Linuxをサポートするために、SwiftではGlibcというライブラリを用意しており、C標準ライブラリをSwiftから扱いやすくしてくれています。Appleプラットフォーム上では同様の機能を持つDarwinというライブラリが以前から用意されていて、これとほぼ互換性を保っています。Swiftではプラットフォームに依存しないために様々な努力を行っており、Swiftというプログラミング言語を広く普及させようという強い意思を感じさせます。

Package Manager

公式にPackage Managerが用意されたことも大きなポイントです。Package Managerでビルドされたライブラリは、Swiftのモジュールという仕組みを使ってネームスペースが分けられます。モジュールを必要な単位で分割することは一般的にはベターです。Package ManagerのマニフェストファイルがSwiftファイルであり、その多くがSwiftで実装されています。それぞれとても興味深い実装になっており、例えばGit.swiftにはaccentのついたsystémという関数があり、Gitのコマンドを実行しています。

import PackageDescription

let package = Package(
    name: "HatenaKit",
    dependencies: [
        .Package(url: "git@github.com:hatena/HatenaCore.git", "1.0.0"),
    ]
)

パッケージを定義するマニフェストファイルの例。これをPackage.swiftとして保存する。依存はGitのURLやセマンティックバージョニングによって管理できる*4

その他

このほかにも、LLDBのSwiftサポートや、SourceKitというコード補完やシンタックスハイライトのためのフレームワークも公開され、IDEの基本的な機能が実現できるようにもなっています。

コミュニティ

公開されたのはソースコードだけではなく、コミュニティも大きく開かれました。メーリングリストやGitHubといったコミュニケーションの場所が用意されたほか、API Design Guidelinesという標準的な命名規則やルールが示されており、コントリビュートの指針になります。そしてSwift Evolutionでは、Swiftという言語そのものの進化が議論されており、コミュニティ側からの提案を受け付けるルールさえ決まっています。実際に、カリー化関数の廃止インクリメント・デクリメント演算子の廃止など、重要な言語仕様の変更が議論されています。この記事を書いている今日現在でも、for..inではないCスタイルのforループの廃止が提案され、正にレビューされています。

Swiftの言語仕様についての提案を受け付ける姿勢や、提案が採択されるまでのフローが最初に整備されている様子は、コミュニティを尊重し共にSwiftを作っていこうという意思の表れのようです。そしてChris Lattnerを含めたAppleのエンジニアたちも、コミュニティと密にやりとりを行っています。実際にコアチームの一人が筆者のTwitterでの発言に対して、聞きたいことはないかとメンションしてくれたこともあります*5。またSwift Evolutionでは、次世代のバージョンであるSwift 3.0に関する情報もいくつか公開されており、あまりAppleらしくないと思ってしまうほどオープンな姿勢を示しています。

Swift 3.0

Swift 3.0についての一番のトピックと言えるのはABIが安定することでしょう。ABIが安定するということは、将来のSwiftのバージョンでコンパイルされたコードとバイナリレベルでの互換性が保証されるということです。今はSwiftのバージョンが変わるとすべてのコードをコンパイルし直す必要があり、そうしないとリンクできません。このため、Swiftの新しいシンタックスに合致しない古いコードのライブラリなどがあると大変面倒です。これが解決されることでSwiftの導入への障壁が一つ減ると考えられます。

このほかにもジェネリクスが完全なものになって、再帰的なprotocolの制約を実現できるようになるようです。またSwiftのAPIをガイドラインに合わせてより一貫性のあるものにすることも予告されています。反面で、並行プログラミングのための構文やC++との相互利用は3.0では実装されないことも決められています。

まとめ

ここまで、Swiftのオープンソース化に伴っての様々なトピックを概観しました。当初の予想を遥かにこえて、Swiftはより広い場面で利用可能なプログラミング言語になろうとしています。実際にSwiftはバランスのとれた使い勝手の良い言語と言えるでしょう。強い静的型付けでありつつ、Optional型を含めたモダンなフィーチャーをひろく取り入れ、オブジェクト指向と関数型プログラミングのいいとこ取りをして、実行速度も十分以上に速い。ほとんど万能とも言えるこの新しいプログラミング言語は、オープンソース化や来年秋に予定される3.0のリリースによって、今後さらに勢いを増していくことでしょう。

Appleプラットフォームのネイティブアプリを作っている方はもちろん、そうじゃない皆さんにとっても、Swiftを学ぶことは新たな発見に満ちた体験となるでしょう。株式会社はてなでは、プログラミング言語Swiftの教科書を公開しています。

try! Swift

try! Swift

2016年3月に東京でSwiftに関するカンファレンスが開催されます。世界中から著名なエンジニアが大勢いらっしゃって、それぞれ素晴らしいトークをしてくださるようです。筆者の id:cockscomb も登壇します。なるべくおもしろいお話ができたらと思っていますので、是非いらしてください。

参考

この記事のほとんどはAppleによるドキュメンテーションや実際のコードを元に書かれています。


明日のアドベントカレンダーは id:tapir320 の担当です。ご期待ください。