massa142's blog

くり返す このポリリズム

「JavaScriptで学ぶ関数型プログラミング」を読んだ

JavaScriptで学ぶ関数型プログラミング

JavaScriptで学ぶ関数型プログラミング

動機

感想

関数型プログラミングのエッセンスを改めて本で学べてよかった。あとはUnderscore.jsを使って解説が進んでいくので、Underscore.jsについてかなり詳しくなれる笑。

ES5しかりUnderscore.jsしかり、2018年のJavaScriptでは必須ではない要素が多いのも事実だけど、関数型の考えとテクニックをわかりやすいコードで体験できるとても良い本だった。

この次は、「付録 A 世の中の関数型JavaScript」でも紹介されているElmを触ってみたくなった。Introduction · Elm Tutorial を今度やってみようと思う。

読書メモ

1章 関数型JavaScriptへのいざない

関数型プログラミングとは、値を抽象の単位に変換する関数を使用して行うプログラミングであり、それらを使ってソフトウェアシステムを構築することである。

  • 関数型のアプローチ: 人間に関するデータの取り扱いに理想的
  • オブジェクト指向のアプローチ: 人間をシミュレートする場合に最適

2章 第一級関数と作用的プログラミング

JavaScriptが第一級関数をサポートしているという事実が、関数型プログラミングの実践を後押しします。

3章 JavaScriptにおける変数のスコープとクロージャ

  • 関数スコープをシミュレートすることで、thisについての理解が深まる
function strangerIdentity(n) {
  // intentionally stranger still
  for(this['i'] = 0; this['i']<n; this['i']++);
  return this['i'];
}

strangerIdentity(108);
//=> 108

i;
//=> 108
// グローバル変数を汚染している

strangerIdentity.call({}, 10000);
//=> 10000

i;
//=> 108
// グローバル変数への汚染は防げたが、このままだとグローバル変数にアクセスできない

function f() {
  this['a'] = 200;
  return this['a'] + this['b'];
}

var globals = {'b': 2};

f.call(_.clone(globals));
//=> 202

globals;
//=> {'b': 2}

クロージャを一言で表すと、自身が定義されたスコープに存在する外部のバインディングを、そのスコープの実行完了後にも使用するために確保している関数のことです。

  • 次はクロージャをシミュレートしてみよう
    • 外側の関数で定義された変数を個別に確保しておき、その変数を、返す関数のthisにバインドする
function createWeirdScaleFunction(FACTOR) {
  return function(v) {
    this['FACTOR'] = FACTOR;
    var captures = this;

    return _.map(v, _.bind(function(n) {
      return (n * this['FACTOR']);
    }, captures));
  };
}

var scale10 = createWeirdScaleFunction(10);

scale10.call({}, [5, 6, 7])
//=> [50, 60, 70];

4章 高階関数

  • 高階関数の定義
    • 第一級データ型である
    • 引数として関数をとる
    • 関数を戻り値として返す
  • 値ではあく、関数を使え
    • どのような引数が渡されても常に定数を返す関数を使用する
  • 関数型スタイルでは実行ターゲットとなるオブジェクトを引数に取る関数を好む
  • 高階関数に渡す引数は返される関数の「設定項目」であると考えてみるとよい

5章 関数を組み立てる関数

  • カリー化
  • カリー化された関数は、論理定な引数が1つ渡されるたびに新しい関数を返す関数
  • 右から左へカリー化することでオプションの引数の個数を固定できるのでオススメ
  • カリー化された関数を使用することによって、「流暢な」インターフェイスを持った関数を生成できる
  • Haskellでは、関数はデフォルトでカリー化されている
  • 部分適用
    • カリー化とは異なり、引数の数は必ずしも一つではなく、複数の引数を扱うこともできる
  • JavaScriptにおいて、関数が可変長の引数を取ることにより複雑化されてしまうカリー化とは異なり、任意の数の引数の部分適用は関数合成の戦略として合理的と言える

6章 再帰

7章 純粋性、不変性、変更ポリシー

  • 関数型プログラミングは、ソフトウェアの創造プロセスにまとわりつく複雑さを最小限に抑えるための構築方法を考える手段
  • 純粋関数
    • 関数自身がコントロールできる範囲外にあるどの変数も変更せず、返さず、そして依存しない
  • Ref: Immutable.js

8章 フローベースプログラミング

  • チェーン
    • 共通の参照をもつメソッドを連続して呼ぶ
      • _.chain()
      • Promise
  • パイプライン
    • 期待されるデータ値が入力されると、非破壊的変換を行い、そして新しいデータを返す
  • アクション
    • 異なる型の戻り値をもったさまざまな関数を合成するテクニック
    • 内部のデータ構造の管理詳細を隠蔽
    • Reduxのactionにも通じる?

9章 クラスを使わないプログラミング

  • Mixin
    • mixinで定義したすべてのメソッドをプロトタイプにコピー
_.extend(Hole.prototype
    , HoleMixin
    , ValidateMixin
    , ObseerverMixin);
  • mixinベースのプログラミングを行うよりも、まずはプリミティブや配列オブジェクトなどのシンプルなデータ型を試みることをお勧め