Identity Functor ってパイプライン演算子っぽい


ふと、「Identity Functorってパイプライン演算子っぽい」と思ったんですよ。何言ってんだって感じですが。とりあえず、JavaScript で下記のような関数を定義するとパイプラインっぽいことができるんじゃないかと思った次第です。

```.js
const wrap = x => ({
  unwrap: x,
  map: f => wrap(f(x)),
  tap: f => wrap((f(x), x))
});
```


# tap と unwrap がある理由

Identity Functor に`tap`は不要です。Scala が`pipe`（上の`map`に対応するメソッド）とともに`tap`も導入したことを参考に入れてみました。副作用が身近な言語なら`tap`は確かに便利ですね。

Scala では、`pipe/tap`を呼び出すと暗黙の変換が発動して元の値が`ChainingOps`に包まれます。また、`pipe/tap`の戻り値は`ChainingOps`に包まれていません。そのため明示的に包んだり、包装をほどいたりする必要はありません。それに比べると、上の JavaScript の実装は明示的な`wrap/unwrap`が必要になります。なぜなら、暗黙的な変換はされませんし、`map/tap`の戻り値が包まれた状態で返されるからです。

余談ですが、`tap`は Ruby が源流です。逆に言うと Ruby の`then`はパイプラインっぽいかもしれませんね。


# map をパイプラインとして使ってみる

次のコードの「通常の関数適用」と「map を使った関数適用」の処理結果はどちらも`202`になります。

```map使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;

// 通常の関数適用
timesTow(addOne(100));

// map を使った関数適用
wrap(100).map(addOne).map(timesTow).unwrap;
```

`map`を使うと関数適用の入れ子が解消され、処理の順にそって関数を左から右に書けています。パイプラインっぽいことは十分できていますね。

`this`を考慮しなければ、`fn(…(f1(f0(x))…)`の形の関数適用の入れ子は一般に、`wrap(x).map(f0).map(f1)….map(fn).unwrap`へ書き換え可能です。


# tap を使ってみる

上の map 使用例のコードに`tap`を挟み込むと、処理途中の結果をコンソールに表示できます。具体的に、処理結果は`202`で変わりませんが、コンソールには`100`, `101`, `202`と途中結果が表示されていきます。

```tap使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;

// tap を使って処理の途中結果をコンソールに出力
wrap(100)        .tap(console.log)
  .map(addOne)   .tap(console.log)
  .map(timesTow) .tap(console.log)
  .unwrap;
```

一次変数を用意したり、引数をそのまま返す関数を用意すれば通常の関数適用でも`tap`と同じ事はできるかもしれません。ただ、`tap`の方がコードの追加は容易でしょうし、不要になった際の除去も簡単でしょう。

ちなみに、応用すると下記のようなおどろおどろしい FizzBuzz を書くこともできます。

```toFizzBuzz.js
const toFizzBuzz = n => wrap(n)
  .map(num => ({num, str: ''}))
  .tap(x => {
    if(x.num % 3 === 0)
      x.str += 'Fizz';
  })
  .tap(x => {
    if(x.num % 5 === 0)
      x.str += 'Buzz';
  })
  .tap(x => {
    if(x.str === '')
      x.str += x.num.toString();
  })
  .map(x => x.str)
  .unwrap;
```


# 関数合成

使いどころは不明ですが、同じような考えで関数合成のみを扱うオブジェクトも生成できます。

```.js
const unary = (f = x => x) => ({
  run: f,
  mappend: g => unary(x => g(f(x))),
  tappend: g => unary(x => {
    const y = f(x);
    g(y);
    return y;
  })
});
```

```mappend_tappend使用例.js
const addOne = x => x + 1;
const timesTow = x => 2 * x;
const compose = unary()
  .tappend(console.log)
  .mappend(addOne)
  .tappend(console.log)
  .mappend(timesTow)
  .tappend(console.log)
  .run;

compose(100);
```

# 型付け

TypeScript で型を意識するなら次のようになるでしょうか。`wrap`は素直に型が付きますね。`unary`は、型が合わないためデフォルト引数を設定できません。別途、`emptyUnary`を用意すれば、同じように`emptyUnary()`を起点に関数を合成していく事ができます。

```.ts
type Identity<A> = {
  unwrap: A,
  map: <B>(f: (x: A) => B) => Identity<B>,
  tap: <U>(f: (x: A) => U) => Identity<A>
}

const wrap = <A>(x: A): Identity<A> => ({
  unwrap: x,
  map: f => wrap(f(x)),
  tap: f => wrap((f(x), x))
});

type UnaryBuilder<A, B> = {
  run: (x: A) => B,
  mappend: <C>(g: (x: B) => C) => UnaryBuilder<A, C>,
  tappend: <U>(g: (x: B) => U) => UnaryBuilder<A, B>
}

const emptyUnary = <A>(): UnaryBuilder<A, A> => unary(x => x);

const unary = <A, B>(f: (x: A) => B): UnaryBuilder<A, B> => ({
  run: f,
  mappend: g => unary(x => g(f(x))),
  tappend: g => unary(x => {
    const y = f(x);
    g(y);
    return y;
  })
});
```

# おわり

まあ、ごちゃごちゃ言ってないで、パイプライン演算子`|>`が使える環境なら、それを使えばいいですし。使えなくても次のような関数を定義すれば十分ですね。

```.js
const pipe = (x, ...fs) => fs.reduce((v, f) => f(v), x);
```