Это является продолжением моей предыдущей вопрос: Sequencing both Scalaz WriterT and Either with for-yieldНастройка состава будущего, будь то и писатель в Scalaz
Следующий блок кода является примером последовательности Future
, Either
и Writer
с использованием Монадой трансформаторов EitherT
и WriterT
; следующий вопрос: как тонко изменить поведение этого пакета трансформаторов.
import scalaz._, Scalaz._
class Example[F[_], L] (val logFn: (String) => L)(implicit val f: Monad[F], l: Monoid[L])
{
type T = Throwable
type EF[α] = EitherT[F, T, α]
type WEF[α] = WriterT[EF, L, α]
private def unreliableInt (i: Int): T Either Int = new java.util.Random().nextBoolean match {
case false => Right (i)
case true => Left (new Exception (":-("))
}
private def fn (i: Int): WEF[Int] = WriterT.put[EF, L, Int](EitherT.fromEither[F, T, Int](f.point (unreliableInt (i))))(l.zero)
private def log (msg: String): WEF[Unit] = WriterT.put[EF, L, Unit](EitherT.right[F, T, Unit](f.point (())))(logFn (msg))
private def foo(): WEF[Int] = for {
_ <- log ("Start")
x <- fn (18)
_ <- log ("Middle")
y <- fn (42)
_ <- log ("End")
} yield x + y
def bar(): F[(Option[Int], L)] = {
val barWEF: WEF[Int] = foo()
// Pull out the logs.
val logsEF: EF[L] = barWEF.written
val logsF: F[L] = logsEF.toEither.map {
case Right (x) => x
case Left (e) => logFn(s"Not the logs we are looking for ${e.getMessage}")
}
// Pull out the value.
val resEF: EF[Int] = barWEF.value
val resF: F[Option[Int]] = resEF.run.map {
case \/- (r) => r.some
case -\/ (ex) => None
}
for {
logs <- logsF
response <- resF
} yield (response, logs)
}
}
object Program
{
def main (args : Array[String]) = {
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global
type L = List[String]
type F[α] = Future[α]
implicit val l: Monoid[L] = new Monoid[L] { def zero = Nil; def append (f1: L, f2: => L) = f1 ::: f2 }
implicit val f: Monad[F] = scalaz.std.scalaFuture.futureInstance
def createLog (s: String) = s :: Nil
val example = new Example[F, L] (createLog)
val result = Await.result (example.bar(), 5 seconds)
println ("Context logs attached:" + result._2.foldLeft ("") { (a, x) => a + "\n$ " + s"$x"})
println ("Result:" + result._1)
}
}
Функция foo
не ведет себя, как мне это нужно для; функция bar
и функция main
иллюстрируют проблему.
желаемое поведение таково, что main
всегда будет печатать один из следующих результатов:
Context logs attached:
$ Start
Result:None
или
Context logs attached:
$ Start
$ Middle
Result:None
или
Context logs attached:
$ Start
$ Middle
$ End
Result:Some(60)
main
функция должна, однако, никогда не печатайте следующее:
Context logs attached:
$ Not the logs we are looking for :-(
Result:None
Но это именно то, что он делает. Когда оба fn1
и fn2
успешны, foo
ведет себя как требуется, и main
распечатывает все журналы. Если оба или оба fn1
или fn2
возвращают номер Left
, функция bar
не возвращает никаких журналов, и основная часть печатает только исключение. Theres никак не увидеть как далеко оно получило в журналах.
кажется, что именно этот стек трансформаторов ведет себя таким образом, что если когда-либо есть в последовательности с -\/
, контекст каротаж просто отображенной из ...
Глядя на код Scalaz для WriterT
этого выглядит скорее всего, будет так:
final case class WriterT[F[_], W, A](run: F[(W, A)])
WriterT
случай класс, только член run
. В отношении этого примера run
является кортежем нашего контекста ведения журнала (A
) и нашего результата, оба завернуты в те же EitherT
(F
). W
и A
связаны данными по типу, так что оба они оба находятся внутри левого или обоих внутри справа.
Я могу предположить, что мне нужно настроенную версию WriterT
, которая ведет себя немного по-другому, сохраняя свои данные немного, как это, предоставляя доступ к писателю части только в свежем Applicative[F].point
:
final case class WriterT[F[_], W, A](wF: F[W], vF:F[A]) {
def run: F[(W, A)] = for {
w <- wF
v <- vF
} yield (w, v)
}
Хотя я» m не совсем уверен, что создание собственного класса классов WriterT
было бы целесообразным подходом к решению этой проблемы и достижению моего желаемого поведения.
Какие у меня варианты?
Но вы не будете получать журналы, если будущее истечет! –