Swift 3.0ウイッシュリスト
Swift 2.0ではプロトコルエクステンションや適切なエラーハンドリングなど大きな改善が施されました。これは、Appleが開発者コミュニティの要望を取り入れつつ、Swiftの未来を創っていこうという姿勢を示しています。そこで私たちは、Swiftを使う開発者に、次のリリースでどんなものが欲しいかを聞いて回ることにしました。型システム、プロトコルやその他のツール群などについて、すばらしい改善案が出てきましたので皆さんと共有します。
静的型付きエラー
私の最初の要望は型付きエラーについてです。これは小さく見えますが重要な拡張です。Swift 2では新しいエラーハンドリング機構が導入されましたが、残念ながら他の言語に比べると強く型付けされているとは言えません。良い点はエラーハンドリングが関数シグネチャの一部になったことです。たとえば、doSomething()
とdoSomething() throws
は違う型で、前者を後者の代わりに使うことはできません。悪い点はdoSomething() throws
はthrowする可能性のあるエラーの型を明示できないことです。(プロトコルのようにthrows<IOType, NetworkingError>としたい)
依存型
2つ目の要望は、依存型のサポートです。私自身まだ依存型の概念に悩み中のなのですが、型システムにおいて非常にクールな機能追加になると考えています。依存型は値そのものに制限をかけることです。型システムは、型無し配列 → 文字列の配列 2 つだけの要素を含む文字列の配列が表現できるように進化してきていますが、そのように言語の表現力が拡張されると便利です。依存型が便利なシチュエーションは下記のような例です。
class Car {
var wheels: [Wheel]<4> = [Wheel(), Wheel()]
// これは型が合わないのでコンパイルエラー、wheelsはWheelが4つなければならない
}
Swift Cocoa Branch
最後の要望はCocoaのSwiftブランチです。おそらくこれは実現が難しいでしょうね。Cocoaは素晴らしいフレームワーク群ですが、レガシーな部分を多く抱えており、それがSwiftの言語としての発展に影響を与えることになでしょう。
まず、参照の必要がない構造体が欲しいです。(ちょっと思い付く限りでは、UIBarButtonItemやUINavigationItemなどのクラスは状態が変わらないので構造体に置き換えられるはずです)そうするとEnum中の関連する値を活用してAPIをリデザインできるようになると思います。Enumは今何が起きているのかをより正確に教えてくれることがあります。たとえば、UIDatePickerでAssociated Enumを使えば、値とその値を作るために使われるモードを一つのプロパティで表現できるようになります。
class UIDatePicker {
enum Value {
case Date(date: NSDate)
case CountDownTimer(period: NSTimeInterval)
}
var value: Value?
}
まぁこれは実現が難しいでしょう。この種の変更は、既存のAPIと新しいAPI両方について新バージョンで別々の開発チームを用意する必要があり、多大な労力がかかります。
現実に起きている問題やSwiftチームが追っている長期の目標に比べると、ここに挙げたアイデアのいくつかはばかげたアイデアに聞こえるかもしれませんね。Swiftチームは本当に素晴らしい仕事をしていて、日々それを実感しています!
ツール群の改善
私が欲しいのはより成熟されたツール群です。その筆頭はデバッガで、時々ではなく、常に信頼できるデバッガが欲しいです。現在のスタックフレームに存在するシンボルの値を常に表示してほしいです。エディタは、少なくともObjective-CでできているようにSwiftでもリファクタリングができるようにしてほしいです。そうすれば、コードをメソッド化して抜き出したり、プロジェクト全体に渡って同じクラスやメソッドの名前を変更できるようになります。
エディタには、お決まりのコードをいちいちタイプする煩わしさから私を開放してほしいです。例えば、私が使おうとしているプロトコルで要求されるメソッドがクラスや構造体に実装されていないことをコンパイラーが検知し、エディタが欠けているメソッドの定義を自動的に補完してくれるようになってくれたらいいですね。
テストの改善
2つめの要望はシンプルですがテストに関わる人すべてを幸せにするものです。シミュレータを使わずにテストを走らせることが可能になってほしいです。もしあるクラスがFoundationのみに依存しているようであれば、そのテストはシミュレータを使うこと無しに実行できるはずです。これにより、テストの総実行時間を削減し、テスト自体を簡単にすることができます。
イントロスペクション
3つめは言語そのものに影響を与えるものです。より改善されたイントロスペクション(リフレクションと呼んでもいいでしょう)とより動的なディスパッチが欲しいです。これらが無ければモッキングフレームワークのようなツールを作ることはできません。
高階型変数
まずこのSwift 3への要望を書き始める前に謝らなければなりません。おそらくこれから私が要望としてあげる機能は、幾多の開発者がSwiftチームに要望していて、あなたも聞き飽きているであろうものです。しかし、それでも未実装の機能の中では最も大切だと思うので、書きますね。
高階型変数はちょっとバイアスがかかった単語なので、我々が好きな関数型プログラミングの例え「ブリトー」を使って説明しますね。ブリトーは中身に何が入っているかに関わらず、みんなが同じように食べることができます。ブリトーは中身を包んでいる容器にしか過ぎないのです。もし、Swiftに対して「ここにオブジェクトがあります。私はそれがブリトーであることだけしか気にしていません。」と言ったらどうでしょう。「中身がステーキであろうが、野菜であろうが、エビであろうが知ったことはありません。それはいずれにしろブリトーなのです!」と。でもSwiftはそんなことは許してくれません。「同じ中身が入っていれば同じブリトーです。ブリトー配列の中身をみて、実装し、オプショナルブリトーを・・・」なんてことをしなければいけないのです。
上記は、Swiftには、まだ表現することができない上層構造の型が存在することを示す、ちょっと変わった例えでした。すべてのインスタンスがちゃんと動くMonadやFunctorの定義を書くことはまだできません。これは新しいSequenceType
を追加するたびに、S: Equatable when S.Element: Equatable
と書かなければならないということです。こんなことを何度も繰り返すうちに本来の意図を型システムに織り込むことが難しくなりますし、プログラマーがミスをしたりバグを作ったりするようになってしまうのです。
Xcodeを切り離す
私は、今のところSwift言語そのものの進化には非常に満足しています。言語チームはコミュニティのニーズを予測し、それに的確に対応するという点において、素晴らしい仕事をしていると思います。
Swift 3.0に向けては、言語をXcodeから切り離してほしいなと強く思います。Swift 2.0はXcode 7に依存しているので、私自身はSwift 2.0に移行できずにいます。Xcode 7がiOS 7のサポートを打ち切ってしまったからです。SwiftのバージョンをXcode(とiOS SDK)のバージョンに関連付けることは、新バージョンへの移行に不必要な障害を作り出しています。
難しいトレードオフで我々を苦しめること無しに、この史上最高の言語を使わせてほしいと思います。
Extensionにストアドプロパティ
Scalaのトレイトのように、エクステンションを使って既存のクラスに新しいプロパティを追加することができたらいいなと思います。ObjCランタイムを使えば現時点でも可能では有りますが、下記のようなコードを書くたびに、何か嫌な予感がするのです。
extension UIView {
var myTag: String? {
get {
return objc_getAssociatedObject(self, "myTag") as? String
}
set(newValue) {
objc_setAssociatedObject(self, "myTag", newValue, UInt(OBJC_ASSOCIATION_RETAIN))
}
}
}
共変なカスタムジェネリクス
組み込み型についてはすでに共変ですが、自分でParty<T>
のような型を作りたいと思ったときには、ゲストリストに違う種類の人々をデフォルトで追加しなければなりません。
class 🎃 {}
class 👻 : 🎃 {}
class 👹 : 🎃 {}
struct Party<T> {
let attendees:[T]
init(attendees:[T]) {
self.attendees = attendees
}
}
func guestList(party:Party<🎃>) -> String {
return party.attendees.description
}
var attendees = Party(attendees:[👻(), 👻()])
guestList(attendees)
Protocol Conformance for Constrained Generics
Swiftのエクステンションには2つのとても便利な機能があります。ひとつ目はSwift 1からあったプロトコルへの準拠です。
extension String: JSONEncodable {
func toJSON() -> JSON { return .String(self) }
}
もうひとつはSwift 2で導入された、エクステンションへの型パラメータ制約の追加です。
extension Array where Element: JSONEncodable {
func toJSON() -> JSON {
return .Array(self.map { $0.toJSON() })
}
}
残念ながらこれら2つを組み合わせて使うこと(型パラメータに制約を持つプロトコルの準拠)はできません。(例えばextension Array: JSONEncodable where Element: JSONEncodable
とは書けません。)これは「プロトコル指向プログラミング」を実践しようとするなら、ジェネリック型を避けるか、苦労して多重定義をしなければならないということです。もしSwift 3でこれができるようになれば、たくさんのコードを書かなくてよくなりますし、プロトコルとジェネリクスがもっと便利になると思います。
言語そのものには特に要望はなく、C#の非同期スタイルの関数があればいいなとは思います。私の一番大きな要望はむしろツールの改善です。XcodeでSwiftを書くのはいまだに骨が折れます。リファクタリング機能が無いなんて、何十年も前のIDEに舞い戻ったみたいです。エディタの改善と、問題発生時のエラー内容の改善がされたら、とてもすばらしいと思います。
また、Optional.Noneを明示的に書かせることでnilを無くしちゃえばいいんじゃないかと思うのですが、これはお酒を飲みながら話すようなトピックですね🍷。
正直Swift 3.0についてよく考えたことはありません。標準ライブラリはとてもよくできていて、もし何か足りないものがあれば、標準ライブラリを利用して作ることができます。
型付きエラーハンドリング
ある関数がthrow
する可能性のあるエラーの型を宣言できたらいいと思います。現時点では、関数のドキュメントにこの情報を書いておくことが良いとされていますが、コンパイラが型情報を持っていたら、と思うのです。そうすれば全てのケースをcatchしなくても、網羅的なエラーハンドリングが実装できるようになります。エラーを投げる関数のAPIも分かりやすくなります。
あなたがSwift 3.0に望むことは何ですか?