Help us understand the problem. What is going on with this article?

SwiftUIで何気なく使っている some を調べてみる

More than 1 year has passed since last update.

モチベーション

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で実装されたものなので有効に活用してみたいところ。

参考

masa7351
Freelance Software Developer.
https://medium.com/@masa7351
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away