scala - 子类型的 Monoid 应用程序不使用追加运算符编译,但在显式调用时工作

标签 scala scalaz monoids

我正在制作一个用于组合重试执行策略的 Monoid,RetryExecutor[T] 是基础类型。我定义了以下基本类型和一个幺半群:

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)

最佳答案

这是一个简单得多的案例中的相同问题:

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]]

理想的解决方案是为返回值类型作为父类(super class)型的子类型提供构造函数。继续我们的 Option 示例,Scalaz 将这些构造函数提供为 somenone:

scala> some(1) |+| some(2)
res3: Option[Int] = Some(3)

scala> some(1) |+| none
res4: Option[Int] = Some(1)

当然,您也可以显式地向上转换值,但是如果您可以避免使用子类型,那么您的生活将会简单得多。

关于scala - 子类型的 Monoid 应用程序不使用追加运算符编译,但在显式调用时工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26184250/

相关文章:

haskell - 如何组合将 bool 值返回给一个函数的函数

json - 使用lift-json序列化和反序列化案例类

android - 如何使 AndroidSchedulers.mainThread 与 Scala Observable 一起工作?

java - 如何从 Java 使用 scalaz.Reader

scala - 遍历 cats/scalaz 中的字符串

arrays - 为什么 Scalaz 中没有 Array 的 Functor 实例

json - ScalaJson没有整数类型?

java - 为什么SparkSQL中org.apache.spark.sql.types.DecimalType的最大精度值为38?

haskell - 单子(monad)的运算符关联性、关联律和值依赖性如何结合在一起?

Haskell,实现幺半群。什么是 Semigroup,为什么它表现得如此奇怪?