flatMapで遊ぶFizzBuzz　「すべてがFになる」

前回は flatMap がどんなものなのかという基礎の部分を考えたね。

#### 前回記事

https://qiita.com/KentaroMorishita/items/e4ecbfdd14b15a501547#array%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9Ffizzbuzz%E3%81%AE%E5%AE%9F%E8%A3%85

今回は、flatMap と前回作った`Box`を活用して、処理のサンプルとしてちょうど良い FizzBuzz の実装をしてみようか。

## FizzBuzzって？

知らない人はそんなにいないと思うけど、

https://wa3.i-3-i.info/word18535.html

こういうやつです。


## `Box`型のおさらい

まず、基本の`Box`型をおさらいしておくよ。前回作った`Box`は、次のように定義した。

```typescript
type Box<T> = {
  map: <U>(fn: (value: T) => U) => Box<U>
  flatMap: <U>(fn: (value: T) => Box<U>) => Box<U>
  "<$>": <U>(fn: (value: T) => U) => Box<U>
  ">>=": <U>(fn: (value: T) => Box<U>) => Box<U>
  getValue: () => T
}

const box = <T>(value: T): Box<T> => ({
  map: (fn) => box(fn(value)),
  flatMap: (fn) => fn(value),
  ["<$>"]: (fn) => box(fn(value)),
  [">>="]: (fn) => fn(value),
  getValue: () => value,
})
```

おなじみの、`Box`型だね！
※ `<$>` は map 用の演算子

今回の `Box` では `>>=` しか使わないので、最低限の実装であればこれだけでも OK です。

```typescript
type Box<T> = { ">>=": <U>(fn: (value: T) => Box<U>) => Box<U> }
const box = <T>(v: T): Box<T> => ({ ">>=": (fn) => fn(v) })
```

## FizzBuzz 処理を表現する型 `F` の定義

次に、FizzBuzz の各処理を表現するために、型 `F` を定義する。この `F` 型は、`[string, number]` のタプルを受け取り、結果を `Box`に包んで返す関数の型だ。

```typescript
type F = (x: [string, number]) => Box<[string, number]>
```

## 各処理の関数を定義する

それぞれの FizzBuzz 処理を`F`型で定義していくよ。

### `log`関数

`log`関数は、FizzBuzz の結果をコンソールに出力しつつ、値を次の処理に渡す。

```typescript
const log: F = ([s, n]) => box([(console.log(s), s), n])
```

### `fizz`関数

`fizz`関数は、数値が 3 の倍数なら`Fizz`を追加し、次に渡す関数。

```typescript
const fizz: F = ([s, n]) => box([n % 3 ? s : s + "Fizz", n])
```

### `buzz`関数

`buzz`関数は、数値が 5 の倍数なら`Buzz`を追加する。

```typescript
const buzz: F = ([s, n]) => box([n % 5 ? s : s + "Buzz", n])
```

### `num`関数

`num`関数は、`Fizz`や`Buzz`がなかった場合、数値そのものを文字列として表示する。

```typescript
const num: F = ([s, n]) => box([s || s + n, n])
```

## FizzBuzz 処理の連結

ここまで定義した関数を連結して、FizzBuzz を表現する関数`fizzbuzz`を作るよ。この関数も、他の処理と同じく型 `F` に準拠しているのがポイントなんだ。これで、`fizzbuzz`自体も他の `F` 型の関数と同じように flatMap で扱えるようになるよ。

```typescript
const fizzbuzz: F = (x) =>
  box(x)
    [">>="](fizz) // 各関数を >>= で連結
    [">>="](buzz)
    [">>="](num)
```

## FizzBuzz を出力する

最後に、1 から 100 までの数値に対して`fizzbuzz`を適用し、各結果を`log`関数で表示してみる。

```typescript
;[...Array(100).keys()].map((i) => fizzbuzz(["", ++i])[">>="](log))
```

## FizzBuzz の全体コード

ここまでの FizzBuzz 実装をまとめると、以下のようになる。

```typescript
type F = (x: [string, number]) => Box<[string, number]>

const log: F = ([s, n]) => box([(console.log(s), s), n])
const fizz: F = ([s, n]) => box([n % 3 ? s : s + "Fizz", n])
const buzz: F = ([s, n]) => box([n % 5 ? s : s + "Buzz", n])
const num: F = ([s, n]) => box([s || s + n, n])
const fizzbuzz: F = (x) =>
  box(x)
    [">>="](fizz) // 各関数を >>= で連結
    [">>="](buzz)
    [">>="](num)

;[...Array(100).keys()].map((i) => fizzbuzz(["", ++i])[">>="](log))
```

## すべてが F になる

型 `F` ですべての関数を統一させることで、`>>=` を通じて処理を次々に連鎖させ、一貫した流れが生まれるんだ。
こうして **「すべてが F になる」** ことで、FizzBuzz のような一連の処理もシンプルに構築できる。

## 7 の倍数を追加する

flatMap の良いところは、既存の処理に新しい処理をブロックのように簡単に追加できること。ここでは、7 の倍数のときに`Jazz`を表示する処理を追加して、`fizzbuzzjazz`として拡張してみよう。

### `jazz`関数

7 の倍数のときに`Jazz`を追加する関数を新たに定義する。

```typescript
const jazz: F = ([s, n]) => box([n % 7 ? s : s + "Jazz", n])
```

## FizzBuzzJazz の連結

flatMap では、このように新しい処理を**ブロックのように繋げて**簡単に拡張ができる。`fizzbuzz`に`jazz`を追加して、7 の倍数も考慮する`fizzbuzzjazz`を作ってみよう。

```typescript
const fizzbuzzjazz: F = (x) =>
  box(x)
    [">>="](fizz)
    [">>="](buzz)
    [">>="](jazz) // 追加
    [">>="](num)
```

こうすることで、新たな処理を既存のコードに追加するのもシンプルに済ませられるね。

## FizzBuzzJazz の全体コード

最後に、7 の倍数`jazz`を追加した FizzBuzzJazz の全体コードをまとめておくよ。

```typescript
type F = (x: [string, number]) => Box<[string, number]>

const log: F = ([s, n]) => box([(console.log(s), s), n])
const fizz: F = ([s, n]) => box([n % 3 ? s : s + "Fizz", n])
const buzz: F = ([s, n]) => box([n % 5 ? s : s + "Buzz", n])
const jazz: F = ([s, n]) => box([n % 7 ? s : s + "jazz", n]) // 追加
const num: F = ([s, n]) => box([s || s + n, n])
const fizzbuzzjazz: F = (x) =>
  box(x)
    [">>="](fizz)
    [">>="](buzz)
    [">>="](jazz) // 追加
    [">>="](num)

;[...Array(100).keys()].map((i) => fizzbuzzjazz(["", ++i])[">>="](log))
```

こうして、`fizzbuzz`に`jazz`をブロックのように繋げて拡張できた。flatMap を使うと、既存コードに新しい処理を簡単に追加できるね！

## おまけ： Array でも同じように書けるよ

この FizzBuzz は Array でも flatMap を使って同じように処理できるんだ。
Box の代わりに Array を使う場合、`box(x)`の代わりに`[x]`で値を Array に持ち上げて、あとはほぼ同じ形で FizzBuzz の処理が書ける。

型 `F` の返す値は `Box<[stirng, number]>` から `Array<[stirng, number]>` に変わってるところがポイントだね。

## Array を使った FizzBuzz の実装

```typescript
type F = (x: [string, number]) => Array<[string, number]> // BoxをArrayに

const log: F = ([s, n]) => [[(console.log(s.trim()), s), n]]
const fizz: F = ([s, n]) => [[n % 3 ? s : s + "Fizz ", n]]
const buzz: F = ([s, n]) => [[n % 5 ? s : s + "Buzz ", n]]
const num: F = ([s, n]) => [[s || s + n, n]]
const fizzbuzz: F = (x) =>
  [x] // box(x)を [x]にする
    .flatMap(fizz) // flatMapで連結
    .flatMap(buzz)
    .flatMap(num)

;[...Array(100).keys()].map((i) => fizzbuzz(["", ++i]).flatMap(log))
```

## Array に`>>=`を追加する

さらに Box で使ってきた`>>=`記法、実は Array でも同じように使えるんだ。
まず、`Array.prototype[">>="]`に`flatMap`を割り当てて、Array でも同じように`>>=`を使えるように設定しよう。

```typescript
declare global {
  interface Array<T> {
    [">>="]: typeof Array.prototype.flatMap
  }
}

Array.prototype[">>="] = Array.prototype.flatMap
```

### ループの map も `>>=` にしてみる

せっかくだし、ついでにループ用の map も flatMap で連結できるようにするために１つ型を追加しておくよ。

```typescript
type X = (s: string, n: number) => Array<[string, number]>
```

この `X` 型を使って、

```typescript
const x: X = (s, n) => [[s, ++n]]
```

という初期値の作成用の関数 `x` を用意しておく。
この `x` を空文字の入った配列から `>>=` で連結するようなイメージ。

```typescript
Array(10).fill("")[">>="](x)
```

とすることで、

```typescript
[
  ["", 1],
  ["", 2],
  ["", 3],
  ["", 4],
  ["", 5],
  ["", 6],
  ["", 7],
  ["", 8],
  ["", 9],
  ["", 10],
]
```

のような、このあとの FizzBuzz 処理に渡すための `Array<[string, number]>` が生成できる。

## Array で `>>=` を使った FizzBuzz の実装

```typescript
Array.prototype[">>="] = Array.prototype.flatMap // 追加

type X = (s: string, n: number) => Array<[string, number]> // 追加
type F = (x: [string, number]) => Array<[string, number]>

const x: X = (s, n) => [[s, ++n]] // 追加
const log: F = ([s, n]) => [[(console.log(s.trim()), s), n]]
const fizz: F = ([s, n]) => [[n % 3 ? s : s + "Fizz ", n]]
const buzz: F = ([s, n]) => [[n % 5 ? s : s + "Buzz ", n]]
const num: F = ([s, n]) => [[s || s + n, n]]
const fizzbuzz: F = (x) =>
  [x]
    [">>="](fizz) // Arrayでも >>= 記法が使える
    [">>="](buzz)
    [">>="](num)

Array(100)
  .fill("")
  [">>="](x) // x で初期化
  [">>="](fizzbuzz)
  [">>="](log)
```

こんなふうに、Array でも Box と同じように`>>=`を使って連結できるんだ。
**Array も Box も「値を保持し、flatMap で連結できる」という共通の概念**を持っているから、同じような記法で書けるというわけだね。

---

### まとめ

今回の FizzBuzzJazz を通して、flatMap や Box を使った処理の拡張が見えてきたと思う。flatMap はシンプルに処理を繋げつつ、必要に応じて新たなロジックを追加しやすい特徴があるんだ。

このように、`Box`や`Array`といった「値を包んだ構造」を使うことで、さまざまな処理が型や構造で整理され、関数の連携がシンプルになる。

ぜひ、これを応用して flatMap の活用をさらに探求してみてほしいね。