我正在制作一个Monoid用于组合重试执行的策略,而RetryExecutor [T]是基于类型的。我已经定义了以下基类型和一个monoid:
trait RetryExecutor[C] {
def retry[T](f: C => T)(context: C): T
def predicate: Option[Throwable]
def application: Unit
val retryEligible: PartialFunction[Throwable, Boolean]
}
object RetryExecutor {
implicit def retryExecutorMonoid[A] = new Monoid[RetryExecutor[A]] {
...
}
和一些基本类型如:
case class LinearDelayingRetryExecutor[C](delayInMillis: Long)(val retryEligible: PartialFunction[Throwable, Boolean]) extends RetryExecutor[C] {
override def predicate: Option[Throwable] = None
override def application = Thread.sleep(delayInMillis)
}
case class RetryWithCountExecutor[C](maximumRetries: Int)(val retryEligible: PartialFunction[Throwable, Boolean])(implicit val logger: Logger) extends RetryExecutor[C] {
var remainingTries = maximumRetries + 1
override def application: Unit = {
remainingTries = remainingTries - 1
}
override def predicate: Option[Throwable] = {
if (remainingTries > 0) None
else Some(RetryingException("Retry count of " + maximumRetries + " exceeded for operation"))
}
}
我可以手动组合它们:
val valid: PartialFunction[Throwable, Boolean] = { case x: TestException => true }
val monoid = RetryExecutor.retryExecutorMonoid[Int]
val x = monoid.append(RetryWithCountExecutor[Int](3)(valid), LinearDelayingRetryExecutor(100)(valid))
但是当我尝试使用追加运算符时:
val x = RetryWithCountExecutor[Int](3)(valid) |+| LinearDelayingRetryExecutor(100)(valid)
我收到编译错误:
[error] /Users/1000306652a/work/src/test/scala/com/foo/bar/RetryExecutorSpec.scala:25: value |+| is not a member of com.foo.bar.retry.RetryWithCountExecutor[Int]
[error] val k: RetryExecutor[Int] = RetryWithCountExecutor[Int](3)(valid) |+| BackingOffRetryExecutor[Int](100)(valid)
答案 0 :(得分:5)
在更简单的情况下,问题出现了同样的问题:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> Option(1) |+| Option(2)
res0: Option[Int] = Some(3)
scala> Monoid[Option[Int]].append(Some(1), Some(2))
res1: Option[Int] = Some(3)
scala> Some(1) |+| Some(2)
<console>:14: error: value |+| is not a member of Some[Int]
Some(1) |+| Some(2)
^
问题(这不是一个真正的问题,而是更多的设计决策)是Monoid
不是协变的 - Monoid[Option[Int]]
并不意味着我有一个Monoid[Some[Int]]
。
理想的解决方案是为子类型提供构造函数,将值类型作为超类型返回。继续我们的Option
示例,Scalaz将这些构造函数提供为some
和none
:
scala> some(1) |+| some(2)
res3: Option[Int] = Some(3)
scala> some(1) |+| none
res4: Option[Int] = Some(1)
当然,您也可以明确地重新定义这些值,但是如果您可以避免使用这些子类型,那么您的生活将变得更加简单。