Upgrade to Pro — share decks privately, control downloads, hide ads and more …

PlayFrameworkでFuture[Either[A, B]]どうするのか問題/ Pla...

tsatow
April 18, 2019

PlayFrameworkでFuture[Either[A, B]]どうするのか問題/ PlayFramework return type problem

PlayFrameworkを使ってると頻繁にFuture[Either[A, B]]と遭遇します。
このままだととても取り回しが辛いのですが、それをどうやって解決したか、という話を第0回ゆるふわ.scalaでしました。

(追記)
投稿してから気づいたけど、この内容だとまるで自分が解決策を考えたかのような印象与えますね。解決策を考えたのは私じゃありません。私は知ったかぶりしてるだけです。

(更に追記)
スライドのタイトルだけ変更しました。
「PlayFramework戻り値の型どうするのか問題」というタイトルが内容と合っていない気がしたので。

tsatow

April 18, 2019
Tweet

More Decks by tsatow

Other Decks in Technology

Transcript

  1. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 3/25 名前 佐藤 貴比呂( さとう たかひろ)

    所属 株式会社アットウェア Scala 歴 1 年(2017/7~2018/6) Twitter @Satoooooooooooo 趣味 釣り/ 柔道/ チアリーディング その他 ガッツあるときYokohama.scala 開催してます 3 / 25
  2. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 8/25 実際に書いてみる。 // subProcess がそれぞれFuture[Either[Error, Unit]]

    を返すとする def process(input: Input): Future[Either[Error, Unit]] for { r1 <- subProcess1(input) r2 <- r1 match { case Right(_) => subProcess2(input) case Left(err) => Future.successful(Left(err)) } _ <- r2 match { case Right(_) => subProcess3(input) case Left(err) => Future.successful(Left(err)) } } yield Right(()) } 3つの処理を順番に実行してるだけなのに なんかとても辛い。 8 / 25
  3. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 13/25 Future とEither の成功と失敗の意味を考える。 Future とEither

    で表現される失敗は何が違う? 業務的な例外はEither それ以外の非チェック的な例外はFuture => 業務的に注目すべきはEither の部分だけ? 13 / 25
  4. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 14/25 実際、Future がフローに影響する箇所はない。 def process(input: Input):

    Future[Either[Error, Unit]] for { r1 <- subProcess1(input) r2 <- r1 match { case Right(_) => subProcess2(input) case Left(err) => Future.successful(Left(err)) } _ <- r2 match { case Right(_) => subProcess3(input) case Left(err) => Future.successful(Left(err)) } } yield Right(()) } 14 / 25
  5. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 16/25 ラッパーをfor 式で使うにはmap やflatMap を実装する必要があるけど Future

    が失敗のときはそのまま Future が成功してEither が失敗のときもそのまま Future が成功してEither も成功したときだけmap する となるように実装してあげれば良さそう。 16 / 25
  6. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 17/25 例えばこんな感じ? case class MyResult[+E, +R](result:

    Future[Either[E, R]]) { def map[RR](f: R => RR): MyResult[E, RR] = MyResult(result.map(_.map(f))) def flatMap[LL >: L, RR](f: R => MyResult[E, RR]): MyResult[E, RR] = MyResult { result flatMap { case Right(r) => f(r).result case Left(err) => Future.successful(Left(err)) } } } 17 / 25
  7. 2019/4/19 PlayFramework 戻り値の型どうするのか問題 127.0.0.1:8080/PlayScalaResult/#1 18/25 実際に使ってみる。 def process(input: Input): Future[Either[Error,

    Unit]] for { _ <- subProcess1(input) _ <- subProcess2(input) _ <- subProcess3(input) } yield Right(()) } 最初の処理と比べてだいぶすっきりしたので めでたしめでたし! 18 / 25