SwiftのReverse GenericsというかOpaque Typeの紹介（社内勉強会用）

# Swiftとは

[Wikipedia](https://ja.wikipedia.org/wiki/Swift_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E)) より

> Swift（スウィフト）は、アップルのiOSおよびmacOS､Linuxで利用出来るプログラミング言語。Worldwide Developers Conference (WWDC) 2014で発表された。アップル製OS上で動作するアプリケーションの開発に従来から用いられていたObjective-CやObjective-C++、C言語と共存することが意図されている。

swift.org [Language Guide](https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html) より

> Swift is a type-safe language, which means the language helps you to be clear about the types of values your code can work with.

---

# Type-safe

```swift
var id: Int = 123
var name: String = "abc"

name = 456 // Compile error: cannot assign value of type 'Int' to type 'String

var ids: Array<Int> = []
ids.append(id)

ids.append("xyz") // Compile 
```

安全に、呼び出し側が指定した型で、Arrayを使用することができる (Generics)

---

# Genericsを使わないと、

```swift
class IntStack {
    var items = [Int]()
    func push(_ item: Int) {
        items.append(item)
    }
    func pop() -> Int {
        return items.removeLast()
    }
}

var ids = IntStack()
ids.push(id)
ids.push(name) // Compile error: cannot convert value of type 'String' to expected argument type 'Int'
```

`StringStack` など、利用する型ごとにクラスが必要？

---

# Genericsを使うと

```swift
class Stack<Element> {
    var items = [Element]()
    func push(_ item: Element) {
        items.append(item)
    }
    func pop() -> Element {
        return items.removeLast()
    }
}

var ids = Stack<Int>()
ids.push(id)
ids.push(name) // Compile error: cannot convert value of type 'String' to expected argument type 'Int'

var names = Stack<String>()
names.push(name) 
```

---

# でも逆に、呼び出し元が決めたい場合も

```swift
// これを公開し、実際のstructは隠蔽する
protocol ChatRoom {
    var id: Int { get }
    var name: String { get }
}

// DM: チャットルーム名は参加者名のカンマ区切り、
fileprivate struct DMChat: ChatRoom {
    var id: Int
    var name: String { memberNames.joined(separator: ", ") }
    var memberNames: Array<String>
}

// チーム: 名前やアバターアイコンを持つ
fileprivate struct TeamChat: ChatRoom {
    var id: Int
    var name: String
    var memberIDs: Array<Int>
    var avatarIcon: String
}

func loadDMChat(id: Int) -> ChatRoom {
    return DMChat(id: id, memberNames: ["Taro"])
}

func loadTeamChat(id: Int) -> ChatRoom {
    return TeamChat(id: id, name: "ACCESS", memberIDs: [100,101,102], avatarIcon: "file")
}

```

`loadDMChat()` では `ChatRoom` として内部の型は隠蔽したいんだけど、実際に返るのは常に `DMChat`。


```swift:これは擬似コード
func loadDMChat(id: Int) -> <C: ChatRoom> C  {
    return DMChat(id: id, memberNames: ["Taro"])
}
```

こんな感じに、Reverse Genericsしたい。

---

# Returning an Opaque Type

```diff
-func loadDMChat(id: Int) -> ChatRoom {
+func loadDMChat(id: Int) -> some ChatRoom {
    return DMChat(id: id, memberNames: ["Taro"])
```

こう書くことで、コンパイル時にこの戻り値は `DMChat` 型とみなされる。

```swift
// Compile error: cannot convert value of type 'some ChatRoom' to specified type 'DMChat'
private let dm: DMChat = loadDMChat(id: 1)
```

仮に `DMChat` 型が呼び出し元に見えていたとしても、その型で受け取ることを許可しているわけではない。

---

# 何が嬉しいのか

- 型の隠蔽
- オーバーヘッドがない
  - どんなオーバーヘッド？

---

# Value type

```swift
var name: String = "abc"
print(name) // abc

var name2 = name

name.append("1")
print(name) // abc1
print(name2) // abc
```

- 実は、SwiftのStringはStructで、値型
- 値渡し、つまり、メモリの確保、コピーなどが行われれる
  - 実際には、Copy-On-Writeなど最適化されていはいる


> https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
> In fact, all of the basic types in Swift—integers, floating-point numbers, Booleans, strings, arrays and dictionaries—are value types, and are implemented as structures behind the scenes.

参照型のシャローコピーにより引き起こされる問題（コピー元も変更されてしまう）の解決として、値の変更の容易さや、イミュータブルクラスを都度まるごと作り直すオーバーヘッドを考え、値型がよいという考え方らしい（？）

---

# 終わり

参考URL

- https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html
- https://heart-of-swift.github.io/protocol-oriented-programming/