モチベーション
var body: some View {
...
}
このコードはもう100回は書いてますが、 some
は Swift5.1で追加された Opaque Result Types の Proposalに準じたものてぐらいの認識で、Proposalの中身をちゃんと理解できていなかった。
SwiftUI簡潔に書けるすげー!! アニメーションめっちゃ簡単に書けるやん!!て感動している今だから、あえて立ち止まって技術について調べたいと思う。
自分の理解を深めるために、記事にしております。
さて、Opaque Result Types何?
Opaqueて、不透明な、明確でない
て意味なんで、 不透明な戻りの型
つまりGenericsみたいなものかと予想はしていた。
実際に、SwiftUIでリストを書こうとすると
var body: some View {
List {
Text("1行目")
Text("2行目")
}
}
レイヤーを重ねたようなデザインにしようとすると、以下のように書ける
var body: some View {
ZStack {
Image("xxxx")
Text("Hello world!")
}
}
戻り値は変わらず常に some View
である。
画面によってデザインが全く異なるものを同一の戻り値として扱っていることになる。
そもそも記事をまとめようとしたきっかけは、Swift 5.1 に導入される Opaque Result Type とは何かにかかれています、『リバースジェネリクス』について記述を見たためです。
protocol Animal {
func say()
}
class Dog: Animal {
func say() {
print("bark bark")
}
}
class Bird: Animal {
func say() {
print("sing sing")
}
}
// ジェネリクス
func say<A: Animal>(_ animal: A) {
animal.say()
}
// リバースジェネリクス ←あくまで概念でコンパイルは通りません
func makeAnimal() -> <A: Animal> A {
return Dog()
}
say(Dog()) // bark bark
makeAnimal().say() // bark bark
- (ジェネリックス) say の 利用者 が A の具体的な型を定め、 say の 実装者は抽象的な A に対してコードを書く。
- (リバースジェネリックス) makeAnimal の 実装者 が A の具体的な型を定め、 makeAnimal の 利用者 は抽象的な A に対してコードを書く。
Swift 5.1 に導入される Opaque Result Type とは何かでは、
とらえどころのない Opaque Result Type ですが、『リバースジェネリクス』という概念を導入すると、
Opaque Result Type は『リバースジェネリクス』のシンタックスシュガー
である とシンプルに考えることができます。 つまり、次の二つが等価だということです。
シンタックスシュガー: 別の構文や記法で記述できるようにしたもの。
// 『リバースジェネリクス』
func makeAnimal() -> <A: Animal> A {
return Dog()
}
// Opaque Result Type
func makeAnimal() -> some Animal {
return Dog()
}
「makeAnimal の 実装者 が A の具体的な型を定め、 makeAnimal の 利用者 は抽象的な A に対してコードを書く」
をSwiftUIに置き換えると以下のように言えると思います。
makeAnimal の 実装者 が A の具体的な型を定め → var body: some View
の中身を書いている我々アプリ開発者が具体的なレイアウトAを定め
makeAnimal の 利用者 は抽象的な A に対してコードを書く → SwiftUIの開発元であるAppleは抽象的な A に対してアプリとして表示できる仕組みを提供する
文章をつなげると、
「var body: some View { ... }
の中身を書いている我々アプリ開発者が 具体的なレイアウトAを定め、SwiftUIの開発元であるAppleは抽象的な A に対してアプリとして表示できる仕組みを提供する」
先に示したリバースジェネリックスの例を some
を使って書き換えて実行してみます。
func makeAnimal1() -> some Animal {
return Dog()
}
func makeAnimal2() -> some Animal {
return Bird()
}
makeAnimal1().say() // bark bark
makeAnimal2().say() // sing sing
どちらも期待通りの結果を出力してくれます。
またAppleのDocumentにも
Returning an Opaque Type
You can think of an opaque type like being the reverse of a generic type.
Opaque Type はGenericsの逆ととらえられるということですね。
まとめ
some
をつけることで 様々なレイアウトの画面を抽象的に扱うことができるようになる。 some
(Opaue Type)はGenericsの逆なのね。
ネットで Opaque Result Types
でググったが実用的な例は見つからなかった。せっかく5.1で実装されたものなので有効に活用してみたいところ。