I'm trying to create some kind of failover behaviour in a composition of "scalaz-core"%"7.2.14" StateT monads.
The StateT monad wraps around EitherT thus it's a monad transformer:
type Error = String
type ErrOrT[T] = Error \/ T
type State[T] = StateT[ErrOrT, String, T]
With these types everything works great - I can leverage the power of Either plus State. I have short-circuit in case of error and can stack my States in one monadic composition:
def func1: State[Int] = ???
def func2: State[Int] = ???
def func3: State[Int] = ???
val stateMonad = for {
res1 <- func1
res2 <- func2
res3 <- func3
} yield res3
In addition I want to create something like tryWithFailover method. It returns original State[T] monad or fallback State[T] monad in case of inner EitherTcontains left:
def tryWithFailover[T](run: State[T])(failover: Error => State[T]): State[T] = ???
So the resulting chain would be:
val stateMonad = for {
res1 <- func1
res2 <- tryWithFailover(func2)(err => failover)
res3 <- func3
} yield res3
If the failover was just a value it would not be a problem. I can use mapT/mapK methods which have an access to inner monad and thus able to check whether the result left or right. In case of left I can recreate inner monad with fallback value. But it's not a value, it's a monad itself and I need something like flatMapT.
Do I miss something, any thought on how it might be done? Failover util function will help me a lot, don't want to break the chain in the middle with explicit run call.
UPD:
Abovementioned failover with value might be like this:
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:
Bad implementation which breaks overall monadic chain with run in the middle:
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
}