LoginSignup
5
2

More than 3 years have passed since last update.

【JavaScript】Promise は Either Monad?

Posted at

イントロダクション

JavaScript における Promise は失敗か成功のどちらかを表し、どちらの場合も何らかの値を持つことができる。さらにメソッドチェーンによりパイプラインのようなものを作ることができ、失敗すると short-circuit する。この点において Promise は Either monad の async 版のような感じがする。ここでは JavaScript の Promise と Scala の Either の対応関係を示す。

Note: 筆者は関数型プログラミングに詳しくない。

constructor

Promise の constructor である Promise.resolvePromise.reject はそれぞれ Scala の RightLeft に対応している。

JavaScript
const r = Promise.resolve(1)  // Resolve(1) と表記する
const l = Promise.reject(0)   // Reject(0)  と表記する
Scala
val r: Either[Int, Int] = Right(1)
val l: Either[Int, Int] = Left(0)

promise.then(f)(1) = either.map(f)

Promise の then メソッドに非 Promise を返す関数を渡したときの動作は Either の map と同じである。

JavaScript
r.then(x => x + 10)  // Resolve(11)
l.then(x => x + 10)  // Reject(0)
Scala
r.map(_ + 10)  // Right(11)
l.map(_ + 10)  // Left(0)

promise.then(f)(2) = either.flatMap(f)

Promise の then メソッドに Promise (一重)を返す関数を渡したときの動作は Either の flatMap と同じである。

JavaScript
r.then(x => Promise.resolve(3))  // Resolve(3)
r.then(x => Promise.reject(3))   // Reject(3)
l.then(x => Promise.resolve(3))  // Reject(0)
l.then(x => Promise.reject(3))   // Reject(0)
Scala
r.flatMap((x: Int) => Right[Int, Int](3))  // Right(3)
r.flatMap((x: Int) => Left[Int, Int](3))   // Left(3)
l.flatMap((x: Int) => Right[Int, Int](3))  // Left(0)
l.flatMap((x: Int) => Left[Int, Int](3))   // Left(0)

promise.catch(f)(1) = either.left.flatMap(f andThen Right)

Promise の catch メソッドに非 Promise を返す関数を渡したときの動作は Either の Left に対してその関数を実行し、さらにその結果を Right で包んで flatten するのと同じである。

JavaScript
r.catch(x => x + 10)  // Resolve(1)
l.catch(x => x + 10)  // Resolve(10)
Scala
r.left.flatMap(((x: Int) => x + 10) andThen Right[Int, Int])  // Right(1)
l.left.flatMap(((x: Int) => x + 10) andThen Right[Int, Int])  // Right(10)

promise.catch(f)(2) = either.left.flatMap(f)

Promise の catch メソッドに Promise (一重)を返す関数を渡したときの動作は Either の Left に対して flatMap を実行するのと同じである。

JavaScript
r.catch(x => Promise.resolve(x + 10))  // Resolve(1)
r.catch(x => Promise.reject(x + 10))   // Resolve(1)
l.catch(x => Promise.resolve(x + 10))  // Resolve(10)
l.catch(x => Promise.reject(x + 10))   // Reject(10)
Scala
r.left.flatMap(x => Right[Int, Int](x + 10))  // Right(1)
r.left.flatMap(x => Left[Int, Int](x + 10))   // Right(1)
l.left.flatMap(x => Right[Int, Int](x + 10))  // Right(10)
l.left.flatMap(x => Left[Int, Int](x + 10))   // Left(10)

まとめ

以上の比較から、Promise の then メソッドは mapflatMap のオーバーロードのようなものであるということがわかる(ちなみに関数の返り値が何重もの Promise になっていたとしても、一気に flatten するという性質がある)。

catch メソッドについてはキレイに Scala コードで表現できなかったが、Left から Right に戻すという操作は関数型言語ではあまりしないのだろうか、と感じた。

5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2