関数型プログラミングのすゝめ

## TL;DR

- コードがシンプルになる
- テストが楽チン
- 副作用がない
- モダンJSは関数型推し
- ```map```、```filter```、```reduce```さえ覚えとけば大体イケる

## きっかけ

React書いてて改めて気になったので。。

## 体系的なコト

関数型プログラミングは宣言型プログラミングの一種らしいです。

- 命令型プログラミング
    - 「どうやって」（**how**）に着目する
    - 昔のC言語みたいな
- 宣言型プログラミング
    - 「何をするか」（**what**）に着目する
    - 最近だとScalaとか



### ex) 書いてみる

- 命令型プログラミング

```javascript
/** 単語を全てハイフンで繋ぐ */
const str = "I have a pen"
let convStr = ""

for (let i = 0; i < str.length; i++) {
    if (str[i] === " ") convStr += "-"
    else convStr += str[i] 
}
console.log(convStr) // I-have-a-pen
```

- 宣言型プログラミング

```javascript
const str = "I have a pen"
const strConnectHyphen = (str) => str.replace(/ /g, "-")

const convStr = strConnectHyphen(str)
console.log(convStr) // I-have-a-pen
```


### 考察

- 命令型プログラミング
    - コメントがないと一見して何をしているか分からない
- 宣言型プログラミング
    - 関数名から何をしてるかすぐに分かる（きちんと命名していれば）

なるほど。。
「やること」（**what**）を宣言的に記述し、
「どうやって」（**how**）の部分を抽象化することでコードが見やすくなるわけですね。


## じゃあ関数型って？

諸説あるようですが、天下の[オライリー](https://www.oreilly.co.jp/index.shtml)様によると、、

- データは全て**イミュータブル**（変更不可）である
- **純粋関数**である

むむ、、なんのこっちゃ。

### ex) イミュータブルなデータ

- ミュータブルな例
    - 元のオブジェクトを変更してしまう（**破壊的メソッド**）

```javascript
const theme_green = {
    title: "darkgreen",
    color: "#006400",
    opacity: 0.5
}
const changeOpacity = (color, opacity) => {
    color.opacity = opacity
    return color
}
console.log(changeOpacity(theme_green, 0.8).opacity) // 0.8
console.log(theme_green.opacity) // 0.8 <- 変わっちゃった
```

- イミュータブルな例
    - 元のオブジェクトは変わらない（**非破壊的メソッド**）

```javascript
const theme_green = {
    title: "darkgreen",
    color: "#006400",
    opacity: 0.5
}
const changeOpacity = (color, opacity) => ({...color, opacity})

console.log(changeOpacity(theme_green, 0.8).opacity) // 0.8
console.log(theme_green.opacity) // 0.5 <- 変わらない（イミュータブル）
```

### 純粋関数

- 少なくとも一つの引数をとり、値もしくは関数を戻り値として返す
- 引数は、**イミュータブル**である
- **副作用**（関数外への影響）が発生しない
- 引数以外の値を参照しない
- 同じ引数ならば、同じ値が返ってくる

### ex) 純粋ではない関数

前述の例です。引数```color```が変更されているので純粋関数ではない

```javascript
const theme_green = {
    title: "darkgreen",
    color: "#006400",
    opacity: 0.5
}
const changeOpacity = (color, opacity) => {
    color.opacity = opacity
    return color
}
```

### ex) 純粋な関数

同じく前述の例。新たなオブジェクトを生成し返しているので、副作用なし

```javascript
const theme_green = {
    title: "darkgreen",
    color: "#006400",
    opacity: 0.5
}
const changeOpacity = (color, opacity) => ({...color, opacity})
```

### 考察

- 要はスコープは狭く、結合度を下げろってことですね
- 引数さえコントロールすればいいので、テストが簡単そう


## 具体的にはどうしろと？

思うと、ES6以降のJSって、関数型で書けって言っているような構文ばかりですよね。
個人的によく使うケースをいくつか。。


### ```map```、```filter```、```reduce```を使い倒す

```javascript
/** raw data */
const theme_colors = [
    {
        title: "darkgreen",
        color: "#006400",
        opacity: 0.5
    },
    {
        title: "tomato",
        color: "#ff6347",
        opacity: 0.7
    },
    {
        title: "navy",
        color: "#000080",
        opacity: 0.5
    },    
]
```

##### 配列から任意の要素を抽出する／削除する
```javascript
/** opacity=0.5の要素を抽出 */
const selected = theme_colors.filter(theme => theme.opacity == 0.5)
```

##### オブジェクトの配列から任意のプロパティだけ抽出する
```javascript
/** カラーコードだけを抽出 */
const selected = theme_colors.map(theme => theme.color)
/** 新しいプロパティにして抽出 */
const selected = theme_colors.map(theme => ({code: theme.color}))
```

##### 配列の任意の要素を変更する
```javascript
/** 特定のタイトルを変更する */
const editThemeTitle = (before, after, arr) =>
    arr.map(item => (item.title == before ? {...item, title: after} : item))
const changed = editThemeTitle("navy", "darkblue", theme_colors)
```

##### メソッドチェーンで繋ぐ
```javascript
/** opacity=0.5の要素のタイトルだけ抽出 */
const selected = theme_colors
    .filter(theme => theme.opacity == 0.5)
    .map(theme => theme.title)
```

##### オブジェクト->配列に変換する
```javascript
const theme_colors = {
    "darkgreen": "#006400",
    "tomato": "#ff6347",
    "navy": "#000080"
}
const arr = Object.keys(theme_colors).map(key => ({
    title: key,
    color: theme_colors[key]
}))
```

##### 配列->単一の値に変換する
```javascript
/** 最大値を求める */
const numbers = [21, 18, 42, 40, 64, 63, 34]
const maxNumber = numbers.reduce((max, number) => (number > max ? number: max), 0)
```

##### 配列->オブジェクトに変換する
```javascript
/** titleをkey, color,opacityをプロパティにもつオブジェクトにする */
const obj_colors = theme_colors.reduce((colors, {title, color, opacity}) => {
    colors[title] = {color, opacity}
    return colors
}, {})
```

### 高階関数を使う

「**関数を引数に取るか、戻り値として関数を返す、もしくはその両方**」を満たす関数
```map```、```filter```、```reduce```は全て高階関数（関数を引数に取るパターン）

##### 戻り値として関数を返すパターン
```javascript
const userLogs = userName => message => console.log(`${userName} -> ${message}`)
/** ロギングを開始する */
const log = userLogs("John Doe")
log("logIn")

/** 非同期処理の完了を通知する */
fetchData
    .then(_ => log("success to load data.")) // John Doe -> success to load data.
    .catch(_ => log("failed to load data.")) // John Doe -> failed to load data.
```

### 再帰関数を使う

関数の中から自分自身を再帰的に呼び出す関数

```javascript
const countDown = (value, fn) => {
    fn(value)
    return value > 0 ? countDown(value-1, fn) : value   
}
countDown(10, value => console.log(value))
```

## まとめ

なんかES6構文の解説みたくなっちゃいました。。
関数型だからこう書く、ということではなく、読みやすいコードを書くことが大事なので
臨機応変にやればいいかなと思います。
多分、純粋関数を制約にしてしまったら、難しい局面とか出てきそう。。
機会があれば、React実践編みたいなのもご紹介できればと思います。

## 参考文献

- [Reactハンズオンラーニング 第2版 ――Webアプリケーション開発のベストプラクティス](https://www.oreilly.co.jp/books/9784873119380/)