CombineのPublishers.Sequenceのmap

Combine.frameworkで遊んでいて `Publishers.Sequence` の `map` が興味深い動作をしたので、雑にまとめておきます。

実行に使った環境はXcode 11.4.1のPlayground（PlatformはiOS）です。

## mapしたものを複数回購読すると

まずは、以下の `PassthroughSubject` の `map` の動作を見てください。

```swift
var cancellables = Set<AnyCancellable>()
let publisher = PassthroughSubject<Int, Never>()

// mapオペレーターで値を10倍する
let kakeru10 = publisher
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }

// 2箇所で購読する
kakeru10.sink { print("A> \($0)") }.store(in: &cancellables)
kakeru10.sink { print("B> \($0)") }.store(in: &cancellables)

// 値を与える
publisher.send(1)
publisher.send(2)
publisher.send(3)
```

これを実行すると出力はこうなります。

```
mapping 1 -> 10
A> 10
mapping 1 -> 10
B> 10
mapping 2 -> 20
A> 20
mapping 2 -> 20
B> 20
mapping 3 -> 30
A> 30
mapping 3 -> 30
B> 30
```

mapによる10倍処理が値1つにつき、2回ずつ実行されてますね。これは2箇所で購読しているためです。ぼくも、まあそうなるだろうと予想していました :slight_smile: 
ここで重い処理や副作用を伴う処理を行うときには注意が必要ですね（それに対する一般的な解決策は記事の後ろに書いておきます）。

ここで、 `publisher` を `Publishers.Sequence` に変更してみます。
2行目の `publisher` を作るところを変更したのと、 `send()` で値を与えていたのを削除しただけです。

```swift
var cancellables = Set<AnyCancellable>()
let publisher = [1, 2, 3].publisher // <======== これ

// mapオペレータで値を10倍する
let kakeru10 = publisher
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }

// 2箇所で購読する
kakeru10.sink { print("A> \($0)") }.store(in: &cancellables)
kakeru10.sink { print("B> \($0)") }.store(in: &cancellables)
```

実行してみます。

```
mapping 1 -> 10
mapping 2 -> 20
mapping 3 -> 30
A> 10
A> 20
A> 30
B> 10
B> 20
B> 30
```

あれ？値1つにつき1回しか10倍処理が実行されていない！？ :open_mouth:

## 何が起こっているのだろう？

`Publisher` には便利なデバッグオペレーター `print` があるので、これを入れてみます。

```swift
var cancellables = Set<AnyCancellable>()
let publisher = [1, 2, 3].publisher

// mapオペレータで値を10倍する
let kakeru10 = publisher
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }
    .print("kakeru10") // <======== これ

print("before subscription") // <======== ついでにこれも足す

// 2箇所で購読する
kakeru10.sink { print("A> \($0)") }.store(in: &cancellables)
kakeru10.sink { print("B> \($0)") }.store(in: &cancellables)
```

次がその出力結果です。

```
mapping 1 -> 10
mapping 2 -> 20
mapping 3 -> 30
before subscription
kakeru10: receive subscription: ([10, 20, 30])
kakeru10: request unlimited
kakeru10: receive value: (10)
A> 10
kakeru10: receive value: (20)
A> 20
kakeru10: receive value: (30)
A> 30
kakeru10: receive finished
kakeru10: receive subscription: ([10, 20, 30])
kakeru10: request unlimited
kakeru10: receive value: (10)
B> 10
kakeru10: receive value: (20)
B> 20
kakeru10: receive value: (30)
B> 30
kakeru10: receive finished
```

なんと、購読が実行される前にマッピングが実行されています。そして、どうやら、あらかじめ10倍されたものを購読しているようです （ `kakeru10: receive subscription: ([10, 20, 30])` ）。

つまり、次のようなことをしたのと同等と考えても良さそうです。

```swift
let kakeru10 = [1, 2, 3]
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }
    .publisher
```


## 型消去すると…

これは `Publishers.Sequence` 型の `map` の実装がそうなっているものと思われるので、 `AnyPublisher` でくるんで型消去すると、一般的な `map` の動作に戻るような気がします。

やってみましょう。

```swift
var cancellables = Set<AnyCancellable>()
let publisher = [1, 2, 3].publisher
    .eraseToAnyPublisher() // <======== これ

// mapオペレータで値を10倍する
let kakeru10 = publisher
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }

// 2箇所で購読する
kakeru10.sink { print("A> \($0)") }.store(in: &cancellables)
kakeru10.sink { print("B> \($0)") }.store(in: &cancellables)
```

結果は以下のように、思ったとおりでした。

```
mapping 1 -> 10
A> 10
mapping 2 -> 20
A> 20
mapping 3 -> 30
A> 30
mapping 1 -> 10
B> 10
mapping 2 -> 20
B> 20
mapping 3 -> 30
B> 30
```

要するに、通常 `Publisher` の `map` は次の関数なのですが、

```swift
public func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>
```

`Publishers.Sequence` には次の関数がオーバーロードされているのです。

```swift
public func map<T>(_ transform: (Elements.Element) -> T) -> Publishers.Sequence<[T], Failure>
```


だから、型消去する前の `kakeru10` （ `map` の戻り値）の型はオーバーロードされた関数の結果の
`Publishers.Sequence<[Int], Never>`
となり、型消去後は
`Publishers.Map<AnyPublisher<Int, Never>, Int>` 
になっていました。

## おまけ：複数回購読する場合の一般的な対策

`Pubisher` を複数回購読する際に、オペレーターによる変換結果を共有させるには `share` や `multicast` などを使う方法があります。

```swift
var cancellables = Set<AnyCancellable>()
let publisher = PassthroughSubject<Int, Never>()

// mapオペレータで値を10倍する
let kakeru10 = publisher
    .map { (value: Int) -> Int in
        let result = value * 10
        print("mapping \(value) -> \(result)")
        return result
    }
    .share() // <======== これ

// 2箇所で購読する
kakeru10.sink { print("A> \($0)") }.store(in: &cancellables)
kakeru10.sink { print("B> \($0)") }.store(in: &cancellables)

// 値を与える
publisher.send(1)
publisher.send(2)
publisher.send(3)
```

↓結果

```
mapping 1 -> 10
A> 10
B> 10
mapping 2 -> 20
A> 20
B> 20
mapping 3 -> 30
A> 30
B> 30
```

下記の記事で詳しくまとめてくださっているので、ここでの説明は省きます（手抜き）。
https://qiita.com/shiz/items/f089c93bdebfaef2196f