iOSDC2018で紹介されていたdynamicMemberLookupに感動したので深掘りする

# iOSDCで参考になった技術を深掘りする

今回は時間の関係で下の英語の記事の一部を翻訳したものを上げます。翻訳の許可を頂いてます。  

[Dynamic Features in Swift](https://www.raywenderlich.com/5743-dynamic-features-in-swift)

Dynamic Member Lookupの箇所です。
理由はあとがきに書いていますが、@dynamicMemberLookupの登場がSwiftコミュニティとPythonコミュニティに大きなインパクトを与える可能性が大きいことです。

チュートリアル用のサンプルファイルは[こちらのリンク](https://koenig-media.raywenderlich.com/uploads/2018/08/DynamicFeaturesInSwift.zip)からダウンロードできます。

## Understanding Dynamic Member Lookup

Swift 4.2の変更に追いついているのであれば、**Dynamic Member Lookup**について聞いたことがあります。 そうでない場合は、ここで概念を学ぶだけでなく様々なことを学ぶでしょう。

チュートリアルのこの部分では、JSONディクショナリから値にアクセスするためにドット表記を使用できるようにする、実際のJSON DSL（Domain Specification Language）を作成する方法の例を見て、Swiftの**Dynamic Member Lookup**のパワーが分かるでしょう。

**Dynamic Member Lookup**を使用すると、コーダーがコンパイル時に存在しないプロパティに対してドットシンタックスで使用できるようになります。要するに、実行時にメンバーが存在し、そのプロセスで読みやすいコードを取得することを信じてコーディングしています。

[この機能の提案](https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md)や[Swiftコミュニティでの関連する会話](https://forums.swift.org/t/se-0195-introduce-user-defined-dynamic-member-lookup-types/8658/10)で述べたように、この機能はPythonのような他の言語との相互運用性をサポートし、データベース実装者やCoreImageなどの文字列型APIに関する定型的なラッパーを作成します。

## Introducing @dynamicMemberLookup

**DogCatcher**ページを開き、コードを確認します。 playgroundでは、DogはDogがDirectionで走っている方法を表します。

dynamicMemberLookupの力で、directionOfMovementとmovingは、それらのプロパティが明示的に存在しなくてもアクセスできます。 犬をダイナミックになる時です。

## Adding dynamicMemberLookup to the Dog

この動的パワーを有効にする方法は、type属性@dynamicMemberLookupの使用することです。

**Directionを返すsubscriptメソッドを追加する**上で下記のコードを追加します。

```DynamicFeaturesIn.swift
subscript(dynamicMember member: String) -> Direction {
  if member == "moving" || member == "directionOfMovement" {
    // Here's where you would call the motion detection library
    // that's in another programming language such as Python
    return randomDirection()
  }
  return .motionless
}
```

☆マークされた行のコメントを外して、dynamicMemberLookupをDogに追加します。☆この行の上にDogのコメントを外します。

directionOfMovementまたはmovingというプロパティにアクセスできるようになりました。後の行に次の行を追加して試してみましょう☆**ここでdynamicDogにdynamicMemberLookup機能を使用してください**：

```DynamicFeaturesIn.swift
let directionOfMove: Dog.Direction = dynamicDog.directionOfMovement
print("Dog's direction of movement is \(directionOfMove).")

let movingDirection: Dog.Direction = dynamicDog.moving
print("Dog is moving \(movingDirection).")
```

playgroundをRunしましょう。 値が残っていることもあれば正しくないこともありますが、最初に表示される2行は次のようになります。

```
Dog's direction of movement is left.
Dog is moving left.
```

## Overloading subscript(dynamicMember:)

Swiftは、異なる戻り型の[添字宣言のオーバーロード](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Declarations.html#//apple_ref/doc/uid/TP40014097-CH34-ID379)をサポートしています。Intを右下に返す添え字を追加することでこれを試してみましょう
**☆Intを返す添字メソッドを追加する：**

```DynamicFeaturesIn.swift
subscript(dynamicMember member: String) -> Int {
  if member == "speed" {
    // Here's where you would call the motion detection library
    // that's in another programming language such as Python.
    return 12
  }
  return 0
}
```

これでspeedというプロパティにアクセスできます。 あなたが以前に追加した以下のmovingDirectionを追加して、勝利を勝ち取ろう：

```DynamicFeaturesIn.swift
let speed: Int = dynamicDog.speed
print("Dog's speed is \(speed).")
```

playgroundをRunしましょう。こんな風に出力されると思います。

```
Dog's speed is 12.
```

かなりいいですね？<font color="Red">@dynamicMemberLookupは、Pythonなどの他のプログラミング言語にアクセスする必要がある場合でも、コードを見栄えの良いものにする強力な機能です。</font>

## Compiler and Code Completion Gone to the Dogs

この動的ランタイム機能と引き換えに、subscript（dynamicMember :)機能に依存するプロパティのコンパイル時チェックのメリットはありません。また、Xcodeのコード補完機能もあなたを助けません。しかし、良いニュースは、プロのiOS開発者が書くよりも多くのコードを読むことです。

**Dynamic Member Lookup**が提供するシンタックスシュガーは、ただ捨てるだけのものではありません。これはSwiftの特定のユースケースと言語の相互運用性を保証し、楽しく素晴らしい機能です。

## Friendly Dog Catcher

**Dynamic Member Lookup**のオリジナルの提案は、特に<font color="Red">Python</font>での言語相互運用性に対処しました。しかし、それが有用な唯一の状況ではありません。

純粋なSwiftユースケースを示すために、**DogCatcher.xcplaygroundpage**にあるJSONDogCatcherコードを操作します。これは、String、Int、およびJSON辞書を処理するために設計されたいくつかのプロパティを持つシンプルな構造体です。このような構造体を使用すると、JSONDogCatcherを作成し、最終的に特定のString値またはInt値を検索することができます。

### Traditional Subscript Method

このような構造体を使用してJSONディクショナリに掘り下げる伝統的な方法は、添字法を使用することです。playgroundにはすでに従来の添字実装が含まれています。 添字メソッドを使用してString値またはInt値にアクセスすると、通常は次のようになり、またplaygroundにも表示されます。

```DynamicFeaturesIn.swift
let json: [String: Any] = ["name": "Rover", "speed": 12,
                          "owner": ["name": "Ms. Simpson", "age": 36]]

let catcher = JSONDogCatcher.init(dictionary: json)

let messyName: String = catcher["owner"]?["name"]?.value() ?? ""
print("Owner's name extracted in a less readable way is \(messyName).")
```

角かっこ、引用符、疑問符を見なければなりませんが、これは機能します。

playgroundをRunしましょう。 次のように表示されます。


```
Owner's name extracted in a less readable way is Ms. Simpson.
```

それはうまく動作しますが、ドットシンタックスを使用するだけで目がより簡単になります。**Dynamic Member Lookup**を使用すると、複数レベルのJSONデータ構造を掘り下げることができます。

### Adding dynamicMemberLookup to the Dog Catcher

Dogのように、JSONDogCatcher構造体にdynamicMemberLookup属性を追加します。

☆JSONDogCatcherを返す添え字（dynamicMember :)メソッドをここに追加してください。

```DynamicFeaturesIn.swift
subscript(dynamicMember member: String) -> JSONDogCatcher? {
  return self[member]
}
```

添字（dynamicMember :)メソッドは既存の添え字メソッドを呼び出しますが、角かっことStringキーを使用する定型コードを削除します。SONDogCatcherの上にこの行のコメントを外してください：

```DynamicFeaturesIn.swift
@dynamicMemberLookup
struct JSONDogCatcher {
```

それで、ドット表記法を使って犬のスピードとオーナーの名前を得ることができます。☆キャッチャーでオーナーの名前とスピードを取得するには、ドット表記を使用してください。

```DynamicFeaturesIn.swift
let ownerName: String = catcher.owner?.name?.value() ?? ""
print("Owner's name is \(ownerName).")

let dogSpeed: Int = catcher.speed?.value() ?? 0
print("Dog's speed is \(dogSpeed).")
```

playgroundをRunしましょう。 コンソールでスピードとオーナーの名前を確認してください：

```
Owner's name is Ms. Simpson.
Dog's speed is 12.
```

あなたはオーナーの名前を持っているので、犬のキャッチャーはオーナーに連絡して、彼女の犬が見つかったことを知らせることができます！

なんて幸せな結末なんでしょう！ 犬とその所有者が再び一緒になり、コードがよりきれいに見えます。ダイナミックなSwiftの力によって、このダイナミックな犬は裏庭のバニーを追うことに戻ることができます。


# あとがき

拙い翻訳で申し訳ございません。
一部、何を言っているのか分からない英語がありましたので適当に日本語っぽい文章にしてみました。(特に☆マークが付いている箇所)

簡単に何がすごいのかと言うと**@dynamicMemberLookup**の登場によってSwiftからPythonコードやライブラリをインポートして使えるようになっただけでなくPythonコードを**Swiftぽくドットシンタックスでプロパティにアクセスできるようになった**と言うことです。

もちろん、Python連携ができるようになることもインパクトが大きいですが、PythonをSwiftで操作するとtypoしやすくなるのでそれをSwiftぽく書けるようになるのは作業効率がとても良くなります。

あとの詳しいことは @koher さんのiOSDC2018の[このLTのプレゼン資料](https://speakerdeck.com/koher/machine-learning-using-python-from-swift-for-ios-engineer)を見ていただけたらわかりやすいと思いました。
(勝手に引用してしまってすみません。)

iOSDC2018の中で個人的に一番感動したのがこのLTでしたので。

で、この@dynamicMemberLookup ともう一つ注目したいのが、
LTで紹介されていた[Swift for TensorFlow](https://github.com/tensorflow/swift)の内容でした。
このライブラリでTensorFlowをSwiftで操作できると言うこと。
コントリビューターにもちろん**Chris Lattner**の名前が入っているのが印象的ですね。

これがうまく言えば、SwiftがPythonをうまく吸収することができて一気に世の中に浸透するんだとか。
早くSwiftが世界のTop1になってほしい自分は早くこのプロジェクトが成功してほしいと思ってます。

ちなみにこのdynamicMemberLookupってこの記事で最初に取り上げてます。

[[Xcode10] Swift4.2でパワーアップしたことについてをまとめました[日本語訳]](https://qiita.com/tmok1160/items/644c9d2e5556f4b1a055)