モジュール安定性はSwift 5.1の最も重要な新機能のひとつだが、Appleのこの言語の最新バージョンには、その他にもProperty WrapperやOpaque Return Type、標準ライブラリの大幅な拡張など、数多くの新機能が含まれている。
思い返せば、Swiftが最初にABI安定性を達成したのはバージョン5.0だった。ABI安定性が目標とするのは、異なるSwiftコンパイラでコンパイルされたコードが一緒に動作することだ。これができれば、言語の構文が変更された場合でも、一定レベルのバイナリ互換性が実現する。ABI安定性には大きな意味が2つある。出荷するアプリケーションにSwiftランタイムのコピーをバンドルする必要がなくなることと、サードパーティ開発者やApple自身がフレームワークをバイナリで配布可能になることだ。この目標はSwift 5.0で達成され、iOS 12.2以降、アプリにSwiftランタイムを含める必要はなくなったが、バイナリ配布可能なフレームワークが現実になるのは、Swift 5.1とiOS 13まで待たなければならなかった。
このABI安定性の要件が、パフォーマンス向上や将来的なABI非互換の言語機能の提供の妨げとなることを防ぐために、Appleは特に注意を払っていた。このメカニズムは、Swift用語でライブラリエボリューションと呼ばれるもので、基本的にはフレームワークが、各タイプをベースとしてABI安定性をオプトアウトできるようになっている。これにより、バイナリ互換性と引き換えに、コンパイル時の最適化を活用することが可能になる。スタンドアロンではない、例えばアプリケーション内にバンドルされるようなフレームワークについては、ABI安定性が問題になることはないため、常に使用可能な最適化がすべて適用された上でコンパイルされる点にも注目すべきだ。
言語レベルでは、Swift 5.1で多くの重要な新機能が導入されている。Property Wrapperは、プロパティの実装パターン定義を一般化して、ライブラリを通じてそれを提供することを目的とするものだ。例えば、Swiftにはlazy
と@NSCopying
という、言語自体に埋め込まれたProperty Modifierがある。コンパイラはこれらのModifierを、対応するプロパティ実装に変換する方法を知っているのだが、このメカニズムは柔軟性がない上に、コンパイラ自体も複雑なものになってしまう。そのためSwift 5.1では、@propertyWrapper
でマークされた特殊なジェネリッククラスが導入された。Property Wrapperは、ラップするプロパティのストレージと、実装本体であるwrappedValue
を提供する。例えば次のコードは、lazy
セマンティクスをProperty Wrapperを使って実装する様子を示したものだ。
@propertyWrapper
enum Lazy<Value> {
case uninitialized(() -> Value)
case initialized(Value)
init(wrappedValue: @autoclosure @escaping () -> Value) {
self = .uninitialized(wrappedValue)
}
var wrappedValue: Value {
mutating get {
switch self {
case .uninitialized(let initializer):
let value = initializer()
self = .initialized(value)
return value
case .initialized(let value):
return value
}
}
set {
self = .initialized(newValue)
}
}
}
Opaque Return Typeは、戻り型のタイプレベルでの抽象化という問題に対処するためのものだ。この機能を使用すれば、ジェネリクスの場合と同じように、構文ではなく、実装によって具体的な型を決定するような、戻り値の抽象化が可能になる。例えば関数の実装で、具体的な型を開示せずにコレクションを返すことができる。これには、次のスニペットのような表記を用いる。
protocol Collection {
func operation1()
func operation2()
}
protocol ApplicationObject {
// a collection used by this type
associatedtype Collection: Collections.Collection
var collection: Collection { get }
}
struct Invoice: ApplicationObject {
var items: some Collection {
return ...
}
}
この機能は、Object-Cでクラスクラスタ(class cluster)として知られているものに近い。その最も分かりやすい例がNSString
クラス(クラスタ)だ。
Swift 5.1では、struct
プロパティのデフォルト値を使用する場合の煩わしさの解決も目指している。struct
のプロパティの一部にデフォルト値がある場合、Swiftコンパイラは、デフォルトイニシャライザ生成時にそれを考慮することができなかった。そのため、イニシャライザ呼び出し時には、すべてのプロパティを指定する必要があった。Swift 5.1では、struct
プロパティにデフォルト値があれば、シグネチャ内でプロパティがそのデフォルトに一致するように、コンパイラがデフォルトイニシャライザを合成してくれる。例えば、次のようになる。
struct Person {
var age: Int = 0
var name: String
// default compiler-synthesized initializer:
// init(age: Int = 0, name: String)
}
let p = Person(name: "Bill")
// Swift 5.0 obliged to specify both arguments:
// let p = Person(age: 0, name: "Bill")
これほど強力ではないが、Swift 5.1の有効な新機能のひとつとして、他の言語で許容されているものと同様の、1行のクロージャを前提とした暗黙的return
がある。これによって例えば、次のような記述が可能になる。
func sum(_ a: Int, _ b: Int) -> Int { a + b }
その他のSwift 5.1の新しい言語機能としては、通常のドット構文を任意の名前に拡張して、実行時にタイプセーフに解決するキーパスメンバ・ルックアップや、クラスメンバとしてのみ可能であったサブスクリプトの定義を拡張したstaticサブスクリプト
およびクラスサブスクリプト
などがある。その他にも改善点として、Ordered Collection Diffing(https://github.com/apple/swift-evolution/blob/master/proposals/0240-ordered-collection-diffing.md)、連続文字列、SIMDサポートの機能追加などがある。