Skip to content

Instantly share code, notes, and snippets.

@ponkotuy
Created July 11, 2019 09:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ponkotuy/4c67ed1d3857ce06548236675cc5a721 to your computer and use it in GitHub Desktop.
Save ponkotuy/4c67ed1d3857ce06548236675cc5a721 to your computer and use it in GitHub Desktop.
EitherT[Future, A, B]のUtil
import cats.data.EitherT
import cats.implicits._
import scala.collection.immutable.Iterable
import scala.concurrent.{ExecutionContext, Future}
object EitherTFutureUtils {
def eitherT[A, B](a: A): EitherT[Future, B, A] = EitherT(Future.successful(Either.right[B, A](a)))
def futureReduceLeft[A, B](xs: Iterable[EitherT[Future, B, A]])(f: (A, A) => A)(implicit ec: ExecutionContext): EitherT[Future, B, A] = {
val result = Future.reduceLeft(xs.map(_.value)) { case (orig, x) =>
for {
a <- orig
b <- x
} yield f(a, b)
}
EitherT(result)
}
def sequence[A, B](xs: Iterable[EitherT[Future, B, A]])(implicit ec: ExecutionContext): EitherT[Future, B, List[A]] = {
val future = Future.sequence(xs.map(_.value)).map {
_.foldLeft[Either[B, List[A]]](Right(Nil)) {
case (Left(b), _) => Left(b)
case (Right(_), Left(b)) => Left(b)
case (Right(list), Right(a)) => Right(a :: list)
}.map(_.reverse)
}
EitherT(future)
}
}
@taisukeoe
Copy link

taisukeoe commented Jul 11, 2019

やりたいことはrightT使えばもっと簡単に書けますよ。
Applicative[Future]の導出に、暗黙のExecutionContextが必要なことに注意。

もし仮に書き換えるならば:

import cats.instances.future._

def eitherT[A,B](a: A)(implicit ctx: ExecutionContext): EitherT[Future, B, A] = EitherT.rightT(a) 

@taisukeoe
Copy link

taisukeoe commented Jul 11, 2019

あとの2つも、Traverse利用すれば簡単になります。
(とはいえ元のIterableの例よりも制約が厳しくなってしまってるので、ここがIterableである必要があるならば別の方法が要りますね…)

import cats._

def futureReduceLeft[T[_]: Traverse, A: Monoid, B](xs: T[EitherT[Future, B, A]])(implicit ec: ExecutionContext): EitherT[Future, B, A] = xs.sequence.map(_.fold) 

def sequence[T[_]: Traverse, A, B](xs: T[EitherT[Future, B, A]])(implicit ec: ExecutionContext): EitherT[Future, B, T[A]] = xs.sequence

@taisukeoe
Copy link

taisukeoe commented Jul 11, 2019

よく考えたら、futureReduceLeftの実装でTraverse#sequenceは不要でした。foldだけで実装できますね。
というわけでもっと簡単になります。

import cats._
import cats.instances.all._

def futureReduceLeft[T[_]: Foldable, A: Monoid, B](xs: T[EitherT[Future, B, A]])(implicit ec: ExecutionContext): EitherT[Future, B, A] = xs.fold

Monoid[A] から Monoid[EitherT[Future, B, A]] を導出できるのが理由です。以下参照。

https://github.com/typelevel/cats/blob/07b2c215fb6830dd909674abee8ce832f9af605b/core/src/main/scala/cats/data/EitherT.scala#L531-L532

https://github.com/typelevel/cats/blob/07b2c215fb6830dd909674abee8ce832f9af605b/core/src/main/scala/cats/instances/future.scala#L39-L40

https://github.com/typelevel/cats/blob/07b2c215fb6830dd909674abee8ce832f9af605b/kernel/src/main/scala/cats/kernel/instances/EitherInstances.scala#L23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment