使用 for-yield 对 Scalaz WriterT 和 Either 进行排序
Sequencing both Scalaz WriterT and Either with for-yield
如果我有:
import scala.concurrent._
import scalaz._, Scalaz._
type Z = List[String]
type F[α] = Future[α]
type WT[α] = WriterT[F, Z, α]
implicit val z: Monoid[Z] = new Monoid[Z] {
def zero = Nil
def append (f1: Z, f2: => Z) = f1 ::: f2
}
implicit val f: Monad[F] = scalaz.std.scalaFuture.futureInstance
我可以这样写代码:
def fooA (): WT[Int] = WriterT.put[F, Z, Int] (f.point (18))(z.zero)
def fooB (): WT[Int] = WriterT.put[F, Z, Int] (f.point (42))(z.zero)
def fooLog (msg: String): WT[Unit] = WriterT.put[F, Z, Unit] (f.point (()))(msg :: Nil))
def foo (): WT[Int] = for {
_: Unit <- fooLog ("log #1")
a: Int <- fooA
_: Unit <- fooLog ("log #2")
b: Int <- fooB
_: Unit <- fooLog ("log #3")
} yield a + b
假设我定义:
type WTT[α] = WriterT[Future, Z, Throwable \/ α]
def flakeyInt (i: Int): Throwable \/ Int = new java.util.Random().nextBoolean match {
case false => i.right
case true => new Exception (":-(").left
}
然后我可以这样写:
def barA (): WTT[Int] = WriterT.put[F, Z, Throwable \/ Int] (f.point (flakeyInt (18)))(z.zero)
def barB (): WTT[Int] = WriterT.put[F, Z, Throwable \/ Int] (f.point (flakeyInt (42)))(z.zero)
def barLog (msg: String): WTT[Unit] = WriterT.put[F, Z, Throwable \/ Unit] (f.point (().right))(msg :: Nil))
def bar (): WTT[Int] = for {
_: Throwable \/ Unit <- barLog ("log #1")
x: Throwable \/ Int <- barA
_: Throwable \/ Unit <- barLog ("log #2")
y: Throwable \/ Int <- barB
_: Throwable \/ Unit <- barLog ("log #3")
} yield {
for {
a <- x
b <- y
} yield a + b
}
有没有办法让 for-yield return 中的 <-
类型为 α
,而不是 \/[Throwable, α]
,这样我就不必手动压平最后扔东西?理想情况下,我想让 bar
函数看起来像 foo
函数,这样我就可以隐藏逻辑中的扁平化错误。
跟进问题:
您应该将您的 Future monad 包装在 EitherT 中,稍微修改您的代码。
它看起来像这样:
type EFT[α] = EitherT[F, Throwable, α]
type WEFT[α] = WriterT[EFT, Z, α]
def bazA(): WEFT[Int] = WriterT.put[EFT, Z, Int](EitherT.right[F, Throwable, Int](f.point(18)))(z.zero)
def bar(): WEFT[Int] = for {
a <- bazA
b <- bazA
} yield a + b
您还可以定义提升函数(将值从一个 monad 提升到您的转换器)以避免样板。
def liftW[A](fa: Future[A]): WLET[A] = {
WriterT.put[MLT, Z, A](EitherT.right[Future, Throwable, A](fa))(z.zero)
}
def bbar(): WLET[Int] = for {
a ← liftW(6.point[F])
b ← liftW(6.point[F])
} yield a + b
我确信 scalaZ 中存在提升函数,但我一直在努力寻找它们,而且看起来有时这些函数更容易自己编写。
如果我有:
import scala.concurrent._
import scalaz._, Scalaz._
type Z = List[String]
type F[α] = Future[α]
type WT[α] = WriterT[F, Z, α]
implicit val z: Monoid[Z] = new Monoid[Z] {
def zero = Nil
def append (f1: Z, f2: => Z) = f1 ::: f2
}
implicit val f: Monad[F] = scalaz.std.scalaFuture.futureInstance
我可以这样写代码:
def fooA (): WT[Int] = WriterT.put[F, Z, Int] (f.point (18))(z.zero)
def fooB (): WT[Int] = WriterT.put[F, Z, Int] (f.point (42))(z.zero)
def fooLog (msg: String): WT[Unit] = WriterT.put[F, Z, Unit] (f.point (()))(msg :: Nil))
def foo (): WT[Int] = for {
_: Unit <- fooLog ("log #1")
a: Int <- fooA
_: Unit <- fooLog ("log #2")
b: Int <- fooB
_: Unit <- fooLog ("log #3")
} yield a + b
假设我定义:
type WTT[α] = WriterT[Future, Z, Throwable \/ α]
def flakeyInt (i: Int): Throwable \/ Int = new java.util.Random().nextBoolean match {
case false => i.right
case true => new Exception (":-(").left
}
然后我可以这样写:
def barA (): WTT[Int] = WriterT.put[F, Z, Throwable \/ Int] (f.point (flakeyInt (18)))(z.zero)
def barB (): WTT[Int] = WriterT.put[F, Z, Throwable \/ Int] (f.point (flakeyInt (42)))(z.zero)
def barLog (msg: String): WTT[Unit] = WriterT.put[F, Z, Throwable \/ Unit] (f.point (().right))(msg :: Nil))
def bar (): WTT[Int] = for {
_: Throwable \/ Unit <- barLog ("log #1")
x: Throwable \/ Int <- barA
_: Throwable \/ Unit <- barLog ("log #2")
y: Throwable \/ Int <- barB
_: Throwable \/ Unit <- barLog ("log #3")
} yield {
for {
a <- x
b <- y
} yield a + b
}
有没有办法让 for-yield return 中的 <-
类型为 α
,而不是 \/[Throwable, α]
,这样我就不必手动压平最后扔东西?理想情况下,我想让 bar
函数看起来像 foo
函数,这样我就可以隐藏逻辑中的扁平化错误。
跟进问题:
您应该将您的 Future monad 包装在 EitherT 中,稍微修改您的代码。 它看起来像这样:
type EFT[α] = EitherT[F, Throwable, α]
type WEFT[α] = WriterT[EFT, Z, α]
def bazA(): WEFT[Int] = WriterT.put[EFT, Z, Int](EitherT.right[F, Throwable, Int](f.point(18)))(z.zero)
def bar(): WEFT[Int] = for {
a <- bazA
b <- bazA
} yield a + b
您还可以定义提升函数(将值从一个 monad 提升到您的转换器)以避免样板。
def liftW[A](fa: Future[A]): WLET[A] = {
WriterT.put[MLT, Z, A](EitherT.right[Future, Throwable, A](fa))(z.zero)
}
def bbar(): WLET[Int] = for {
a ← liftW(6.point[F])
b ← liftW(6.point[F])
} yield a + b
我确信 scalaZ 中存在提升函数,但我一直在努力寻找它们,而且看起来有时这些函数更容易自己编写。