Open4

関数型プログラミング

haseyuyhaseyuy

関数型プログラミングの考え方

  • 「データに何らかの処理を加えていく」の連続で組み立てていくこと
  • 特徴としては以下がある
    • 関数がファーストクラスであること(関数を変数に代入したり、関数の引数や戻り値を使った関数を使うことができる)
    • 変数は再代入禁止である
    • 関数は参照透過性が保たれている(副作用がない)
      例:データ -> データ -> データ
  • 関数型に書き換えるステップ
    • 状態と振る舞いを分ける
    • ループではなくmapやreduceを使う
    • 振る舞いを副作用のない関数にバラす
haseyuyhaseyuy

副作用

結果が引数以外に依存して決まる処理のこと

参照透過性(Referential transparency)

ある式が参照透過であるとは、その式の値を置き換えてもプログラムの振る舞いが変わらない(言い換えれば、同じ入力に対して同じ作用と同じ出力を持つプログラム)ことを言う。

https://ja.wikipedia.org/wiki/参照透過性

type Num = {
  value: number
}
// 関数は同じ変数を引数として与えれれば同じ値を返す
const increment = (a: Num) => ({ value: a.value + 1 })
const one = { value: 1 }
const two = increment(one)
console.log(one, two)
haseyuyhaseyuy

抽象的な関数から具体的な関数を作る

関数合成

関数合成とは複数の関数から一つの関数を作る操作

const add1 = (n: number) => n + 1
const times2 = (n: number) => n * 2
console.log(add1(times2(5))) //1

「関数を合成して合成した関数を返す関数」を定義する

// 二つの関数を受け取って合成して関数を返す関数
const compose = (
  a: (n: number) => number,
  b: (n: number) => number,
) => (x: number) => a(b(x))

// +1する関数
const add1 = (n: number) => n + 1

// *2する関数
const times2 = (n: number) => n * 2

const add10Times2 = compose(add1, times2)
console.log(add10Times2(5)) // 5 * 2 + 1 -> 11

// ...スプレッド構文で可変長引数
// 引数を左から評価するようにする
const pipe = (...fns: Array<(n: number) => number>) =>
(n: number) => fns.reduce((v, f) => f(v), n)

const minue3 = (n: number) => n - 3

const enhancer = pipe(add1, times2, minue3)
console.log(enhancer(5)) // (5 + 1) * 2 - 3 -> 9
haseyuyhaseyuy
type Container<T> = {
  value: T
}

type Of = <T>(val: T) => Container<T>

type MapContainer = <T, U>(f: (t: T)=> U) => (fa: Container<T>) => Container<U>

const map: MapContainer = (f) => ({ value }) => of(f(value))
const of: Of = (value) => ({value})

const toStrLen =(s: string) => s.length
const c = of("hoge")
console.log(c)

const c1 = map(toStrLen)(c)
console.log(c1)