Folktale 学習帳

# はじめに
JavaScriptで関数型プログラミングをするために、二つのライブラリが必要。

- ramda: `compose`, `map` などのツールセットを提供する
- folktale: Maybe, Task などのモナドを提供する

本稿では、ramda, folktale を利用して実践的な JavaScriptアプリを作るための基礎を固める。ramda, folktale ともに fantasy-land spec を意識して実装されているため、相互運用が可能。

以下の章では、断りなく以下の import を利用する。

```js
const R = require('ramda');
const Result = require('folktale/result');
const Task = require('folktale/concurrency/task');
```

# #01. 例外が送出される同期関数
例として `JSON.parse` を考えよう。例外を送出する関数は impure であるため、ほぼ全てのライブラリ関数の呼び出しは impure である。`Result.try`を使って例外を捕捉して、`Result`にする。`Result`はfolktale のクラスで、 一般的には Either と呼ばれるモナドである。

```js
Result.try(() => JSON.parse('{"one": 1}')); // Result.Ok({ value: { one: 1 } })
```

`Result.try` は実行する関数に引数を渡せないし、即時に評価されてしまうので、以下のようにラップしてあげるのが良い。

```js
const myparse = val => Result.try(() => JSON.parse(val));
myparse('{"one": 1}');
```

`JSON.parse` は例外を出す以外は pure なのでこの処方でOKである。一方、例外以外の副作用がある場合、すなわち IO モナドを利用するような場合は、`Task`を使うことになる。次の章で扱おう。


# #02. 副作用のある同期関数
例として `val => JSON.parse(val + Date.now())`  を考えよう。引数に String を渡すと例外が返るし、Date.now()は副作用を持つので、例としては十分実践的だろう。

こういう場合、`Task` モナドにする。Task は IO モナドおよび Promise の上位互換である。`run()`がトリガとなって実行が始まる。

```js
const impure = val => JSON.parse(val + Date.now());
const mydate = val => Task.task(r => r.resolve(Result.try(() => impure(val))));
mydate(0); // Task will be Result.OK Integer
mydate('a'); // Task will be Result.Error Error
```

上記の処方は、TaskにResultが埋まっているので扱いがめんどくさかったり、Result.Error の値が resolve されてしまうなど不満があるかもしれない。そんな時はこんな感じに、Result を Task にconvert してもよい。（こういったユーティリティ関数があってもいいのだが、見つけられていない）

```js
const mydate2 = val => Task.task((r) => {
  const result = Result.try(() => impure(val));
  result.matchWith({
    Ok: ({ value }) => { r.resolve(value); },
    Error: ({ value }) => { r.reject(value); },
  });
});
mydate2(0).run() // -> resolved Task Integer
mydate2('a').run() // -> rejected Task Error
```

以上をまとめる。同期的でimpureな関数 `impure` を使いたいときは、以下のようにする。

```js
const Task = require('folktale/concurrency/task');
const Result = require('folktale/result');
const syncFuncInvoker = func => val => Task.task((r) => {
  const result = Result.try(() => func(val));
  result.matchWith({
    Ok: ({ value }) => { r.resolve(value); },
    Error: ({ value }) => { r.reject(value); },
  });
});
const impure = val => JSON.parse(val + Date.now());
const mydate =  syncFuncInvoker(impure);
mydate(0).run()
mydate('a').run()
```

応用。複数のTaskモナドの結果を一つのTaskにまとめる(Sequenceの利用)。

```js
const R = require('ramda');
const Task = require('folktale/concurrency/task');
const Result = require('folktale/result');

const syncFuncInvoker = func => val => Task.task((r) => {
  const result = Result.try(() => func(val));
  result.matchWith({
    Ok: ({ value }) => { r.resolve(value); },
    Error: ({ value }) => { r.reject(value); },
  });
});

let state = {
  mizuho: { balance: 1000 },
  ufj: { balance: 500 },
};

// impure
const getBalanceFrom = bank => state[bank].balance;
const mizuho = syncFuncInvoker(getBalanceFrom)('mizuho');
const ufj = syncFuncInvoker(getBalanceFrom)('ufj');
const resona = syncFuncInvoker(getBalanceFrom)('resona');

R.sequence(Task.of, [mizuho, ufj]).map(R.sum).run().promise()
  .then(console.log); //-> 1500

R.sequence(Task.of, [mizuho, ufj, resona]).map(R.sum).run().promise()
  .then(console.log)
  .catch(({ message }) => console.log(message));
  // ->  Cannot read property 'balance' of undefined
```

# #03. コールバック型関数の呼び出し

コールバック型の関数は非同期に実行され、呼び出し元には何も返さず、コールバックの中で色々やるので impure な関数である。前章同様、`Task` にしてあげればよい。folktale では、Node型のコールバック型関数、つまりコールバックの第一引数がerrであるような関数を`Task`に変換する`fromNodeBack`という関数があるのでそれを使う。

```js
const nodebackFromTask = require('folktale/conversions/nodeback-to-task');
const sample = (str, cb) => cb(null, `${str} received`);
const genTask = nodebackFromTask(sample);
genTask('hello').run().promise().then(console.log);
```

`setTimeout` などNode型でないコールバックの場合は自分で作ればいい。そんなに難しくはない。

```js
const genTask2 = (val) => Task.task((r) => {
  setTimeout(() => { r.resolve(`${val} received`); } ,1000);
});
genTask2('world').run().promise().then(console.log);
```

# #04. Promise型関数の呼び出し

`Task` は完全にPromiseを包含する。Promise型の関数呼び出しを`Task`に変更するユーティリティがあるのでそれを使えばよい。

```js
const promisedToTask = require('folktale/conversions/promised-to-task');
promisedToTask(() => Promise.resolve(10))().run().promise()
  .then(console.log);
```

# そのあとは？

Result や Task といったモナドにしてしまえばあとは普通に関数型にプログラムを作っていけばよい。