使用Scalaz中的故障转移的StateT [Either]

时间:2017-09-01 09:41:57

标签: scala scalaz

我试图在failover StateT monad的组合中创建某种"scalaz-core"%"7.2.14"行为。

StateT monad环绕着EitherT,因此它是一个monad变换器:

type Error = String
type ErrOrT[T] = Error \/ T
type State[T] = StateT[ErrOrT, String, T]

使用这些类型一切都很好 - 我可以利用EitherState的力量。如果出现错误,我会发生短路,并且可以将我的状态叠加在一个monadic组合中:

def func1: State[Int] = ???
def func2: State[Int] = ???
def func3: State[Int] = ???

val stateMonad = for {
  res1 <- func1
  res2 <- func2
  res3 <- func3
} yield res3

另外我想创建类似tryWithFailover方法的东西。如果内部State[T]包含左侧,则返回原始State[T] monad或后备EitherT monad:

def tryWithFailover[T](run: State[T])(failover: Error => State[T]): State[T] = ???

因此产生的链将是:

val stateMonad = for {
  res1 <- func1
  res2 <- tryWithFailover(func2)(err => failover)
  res3 <- func3
} yield res3

如果failover只是一个值,那就不会有问题了。我可以使用mapT/mapK方法访问内部monad,从而能够检查结果是左还是右。如果离开,我可以重新创建具有fallback值的内部monad。但它不是一个价值,它本身就是一个单子,我需要像flatMapT这样的东西。

我是否会错过任何关于如何做的事情?故障转移实用程序功能对我有很大的帮助,不想通过明确的run调用打破中间链。

UPD:

上述有价值的故障转移可能是这样的:

def tryWithFailover[T](run: State[T])(failover: Error => T): State[T] = {
  for {
    lockedState <- State(st => (st, st))
    result <- run.mapT[ErrOrT, T, String] {
      case ok@ \/-(_) => ok
      case -\/(error) => \/-((lockedState, failover(error)))
    }
  } yield result
}

UPD2:

错误的实现会破坏整个monadic链中间的run

def tryWithFailover[T](run: State[T])(failover: Error => State[T]): State[T] = {
  for {
    lockedState <- State(st => (st, st))
    result <- run.mapT[ErrOrT, T, String] {
      case ok@ \/-(_) => ok
      case -\/(error) => failover(error).run(lockedState)
    }
  } yield result
}

1 个答案:

答案 0 :(得分:0)

经过一些考虑,我得出结论,提到的方法并不坏:

def tryWithFailover[T](run: State[T])(failover: Error => State[T]): State[T] = {
  for {
    lockedState <- State(st => (st, st))
    result <- run.mapT[ErrOrT, T, String] {
      case ok@DRight(_) => ok
      case DLeft(error) => failover(error).run(lockedState)
    }
  } yield result
}

可能有一天有人会纠正我的回答。