Edited at

JavaScriptでEither

More than 1 year has passed since last update.

JavaScript + 関数型プログラミングメモ

folktale v2においては Either は Result という名前になっている。Leftは、 Result.Error, Right は Result.Ok という名前になっている。まずは Either を作る。

const assert = require('assert');

const Result = require('folktale/result');
// create either
{
const rights = [
Result.Ok(1),
Result.Ok({ one: 1 }),
];
rights.forEach((d) => {
assert(d instanceof Result.Ok);
});
const lefts = [
Result.Error(1),
Result.Error({ one: 1 }),
];
lefts.forEach((d) => {
assert(d instanceof Result.Error);
});
}

例外throw型の関数は、 Result.try() を使って関数の返り値or送出された例外をEitherにすることができる。Eitherは mapとmapErrorで、それぞれの成功状態を変更せずに、値を変更できる。matchWith でパターンマッチング、条件分岐ができる。mergeを使うと、成功状態を問わず値を取り出すことが出来る。Eitherが Result.OkResult.Error かは、hasInstanceOf で判別することができる。

// work with either

{
const f = (arg) => {
if (arg) return arg;
throw Error('falsy');
};
assert.deepEqual(
Result.Ok('HELLO!'),
Result.try(() => f('hello'))
.map(str => str.toUpperCase()).map(str => str.concat('!')),
);
assert.deepEqual(
Result.Error(Error('falsy')),
Result.try(f)
.map(str => str.toUpperCase()).map(str => str.concat('!')),
);
assert(Result.Ok.hasInstance(Result.Ok('hello')));
assert(Result.Error.hasInstance(Result.Error('hello')));
const getName = (id) => {
if (id === 1) return 'alice';
throw Error('no such user!');
};
Result.try(() => getName(1)).matchWith({
Ok: ({ value }) => { assert.equal('alice', value); },
Error: () => { throw Error('cannot reach here'); },
});
Result.try(() => getName(2)).matchWith({
Ok: () => { throw Error('cannot reach here'); },
Error: ({ value }) => { assert.deepEqual(Error('no such user!'), value); },
});
assert.equal(
'guest',
Result.try(() => getName()).mapError(() => 'guest').merge(),
);
assert.equal(
'alice',
Result.try(() => getName(1)).mapError(() => 'guest').merge(),
);
}