JavaScript で Maybe(Option/Optional/Nullable)

JavaScriptで関数型プログラミングをしてみようかな、と思う今日この頃。ざっくりいって Ramda と folktale の二つを使いこなせばいいんじゃない？って [StackOverflow が教えてくれた](https://stackoverflow.com/questions/33027305/why-are-folktale-and-ramda-so-different) 。あとドキュメントとして fantasy-land。

とりあえず、null の扱いがあんまり好きじゃないので、 Maybe から始めてみる。Scala でいうところの Option。まずはMaybeを作るところから。`Just`, `Nothing`, `fromNullable()` の三つの方法を試してみた。`Just(null)` は `Nothing` にならないところが注意。nullを渡して`Nothing` にしたい場合は `fromNullable()` を使おう。

```js
const { Just, Nothing, fromNullable } = require('folktale/maybe');
const assert = require('assert');

// Create Maybe
{
  const justs = [
    Just(1),
    Just({ hello: 'world' }),
    Just(null), // you can pass any falthy. returns Just whose value is falthy
    Just(undefined),
    Just([]),
    Just(''),
  ];
  justs.forEach((m) => {
    assert(m instanceof Just);
  });
  const nothings = [
    Nothing(),
    Nothing(null), // any argments will be ignored.
  ];
  nothings.forEach((m) => {
    assert(m instanceof Nothing);
  });
  const js = [
    fromNullable(1),
    fromNullable({ one: 1 }),
    fromNullable(''),
    fromNullable([]),
  ];
  js.forEach((m) => {
    assert(m instanceof Just);
  });
  const ns = [
    fromNullable(null),
    fromNullable(undefined),
  ];
  ns.forEach((m) => {
    assert(m instanceof Nothing);
  });
}
```

Maybeを作ったら、Maybe を変換する関数を作って、`map` と `chain`をつなげてどんどん変換していく。`map`は引数に値を返す関数をとるのに対して、`chain`では引数に明示的にMaybeを返す関数をとる。自分で実装するときは`chain`を使うのがいいのかなあ？と思ったりした。Maybeから値を取り出すには `getOrElse` を使う。`Nothing`に対して処理をしたい場合は`orElse`を使う。

```js
const { Just, Nothing, fromNullable } = require('folktale/maybe');
const assert = require('assert');
// Transform Maybe
{
  const funcForChain = val => Just(val + 2);
  assert.equal(3, Just(1).chain(funcForChain).getOrElse(null));
  assert.equal(2, Just(null).chain(funcForChain).getOrElse(null)); // NOTE: null + 2 = 2
  assert.equal(null, Nothing().chain(funcForChain).getOrElse(null));

  const funcForMap = val => val + 2;
  assert.equal(3, Just(1).map(funcForMap).getOrElse(null));
  assert.equal(2, Just(null).map(funcForMap).getOrElse(null));
  assert.equal(null, Nothing().map(funcForMap).getOrElse(null));

  const getName = obj => ('name' in obj ? Just(obj.name) : Nothing());
  const getDefaultName = () => Just('anon');
  assert.equal(
    'Alice',
    Just({ name: 'Alice' }).chain(getName).orElse(getDefaultName).getOrElse(null),
  );
  assert.equal(
    'anon',
    Just({}).chain(getName).orElse(getDefaultName).getOrElse(null),
  );
  const toUpper = str => Just(str.toUpperCase());
  assert.equal(
    'ALICE',
    Just({ name: 'Alice' }).chain(getName).orElse(getDefaultName)
      .chain(toUpper)
      .getOrElse(null),
  );
  assert.equal(
    'ANON',
    Just({}).chain(getName).orElse(getDefaultName)
      .chain(toUpper)
      .getOrElse(null),
  );
  assert.equal(
    'anon',
    Just({}).chain(getName).chain(toUpper)
      .orElse(getDefaultName)
      .getOrElse(null),
  );
}
```

「値を Maybe に詰め込んで、 Maybe の世界で map と chain で次々と処理していく」という処理パターンは、「値を Promise に詰め込んで、Promise の世界で then で次々と処理していく」という処理パターンと類似しているという [ブログ記事](https://jrsinclair.com/articles/2016/marvellously-mysterious-javascript-maybe-monad/) があって、なるほどなあ、と思った。