JavaScript
babel

関数型プログラミングの世界からやってきた Pipeline Operator

Pipeline Operator

まずはこちらを

https://github.com/tc39/proposal-pipeline-operator

Elixirとかで見かける |> です。
現在はStage 1 Draftで、babelでもう導入可能です。

https://babeljs.io/docs/en/babel-plugin-proposal-pipeline-operator


Usage

https://github.com/kashira2339/pipeline-operator-example

const hello = str => str + "hello";

const space = str => str + " ";

const world = str => str + "world";

const exclamationAsync = async str =>
  new Promise((resolve, reject) => setTimeout(() => resolve(str + "!"), 300));

const toUppercase = str => str.toUpperCase();

const str = "";

const msg1 = world(space(hello(str)));
const msg2 = str |> hello |> space |> world;
const msg3 = str |> hello |> space |> world |> exclamationAsync |> await |> toUppercase;

console.log("msg1:", msg1); // => msg1: hello world
console.log("msg2:", msg2); // => msg2: hello world
console.log("msg3:", msg3); // => msg3: HELLO WORLD!
// msg3のawaitは2018/12/14現在未実装

挙動は単純で、 x |> fとあるとき値xを関数fの第一引数として渡す」となります。

Example Use Cases

リポジトリのWikiにもいくつか載っており、それぞれが有用です。
https://github.com/tc39/proposal-pipeline-operator/wiki/Example-Use-Cases

validation

独立したバリデーション用の関数を組み合わせる

const required = () => value => {
  if (!value) throw Error('required');
  return value;
}
const format = regex => value => {
  if (!regex.test(value) ) throw Error('invalid format');
  return value;
}

value
  |> required()
  |> format(/https?:\/\//)
  |> console.log

React + Redux + react-redux + redux-form + react-router

また、react-reduxを採用したプロジェクトで散見される以下のようなコードも

const Component = () => {
  return ...
}

function mapStateToProps(state) {
  return {
    ...
  }
}

function mapDispatchToProps(dispatch) {
  return {
    ...
  }
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(reduxForm({ ... })(Component)))

export部分で関数のネストがみられるので、pipeline operatorで以下のように改善できる

// before
// export default withRouter(connect(mapStateToProps, mapDispatchToProps)(reduxForm({ ... })(Component)))

export default Component
    |> reduxForm({ ... })
    |> connect(mapStateToProps, mapDispatchToProps)
    |> withRouter

まとめ

pipeline operatorが使えるようになると、
関数を連鎖させるときの可読性が従来と比べて大きく向上するので、
それぞれの関数をコンポーザブルに保ち、用途に応じて合成していくことで
機能を実現していく設計の有用性がより増してきます。

昨今のJavaScriptや周辺ライブラリを追っていると、
Array.prototype.flat,flatMapであったり
Partial Application Syntaxであったり
React hooksであったり
関数型言語にみられるような特徴を持たせるような提案がボチボチみられます。
開発環境やビジネス要求の上でも変化の激しいフロントエンド開発において
関数型プログラミングの思想を取り入れることで参照透過性を高め、
テストのしやすさ=変化への強さを担保していきたいといった時代の流れなのでしょうか。

そうなってくると、
elmのようなプログラミング言語の採用率が上がって行ったりするのか、
はたまた、関数型プログラミングにハードルを感じるプログラマが増え、
より簡単に素早くアプリケーションの作成ができるNuxtがユーザーを増やしていくのか、
いずれにせよ興味深いです。