scala - 发散的隐式扩张

标签 scala typeclass implicit

我有一个关于隐式解析的问题。 比如说,我有以下类型类:

trait Foo[In <: Base, Out <: Base] {
  def factor : Double
}

sealed trait Base
object Base {
  implicit def symmetricFoo[In <: Base, Out <: Base](implicit foo : Foo[In, Out]) : Foo[Out, In] =
    new Foo[Out, In] {
      def factor : Double = 1.0 / foo.factor
    }

  implicit def transitiveFoo[In <: Base, Mid <: Base, Out <: Base](implicit foo1 : Foo[In, Mid], foo2 : Foo[Mid, Out]) : Foo[In, Out] =
    new Foo[In, Out] {
      def factor : Double = foo1.factor * foo2.factor
    }
}

case object A extends Base

case object B extends Base {
  implicit def bFoo : Foo[B.type, A.type] =
    new Foo[B.type, A.type] {
      def factor : Double = 2.0
    }
}

case object C extends Base {
  implicit def cFoo : Foo[C.type, A.type] =
    new Foo[C.type, A.type] {
      def factor : Double = 3.0
    }
}

case object D extends Base {
  implicit def dFoo : Foo[D.type, C.type] =
    new Foo[D.type, C.type] {
      def factor : Double = 5.0
    }
}

如果我在从 X 到 Y 的路径上有中间 Foo,我希望能够获得 Foo[X, Y] 的实例。有时它确实有效,例如。克。

println(implicitly[Foo[D.type, A.type]].factor) // 15.0 (D->C, C->A = 5 * 3)
println(implicitly[Foo[D.type, B.type]].factor) // 7.5 (D->C, C->A, A->B = 5 * 3 * 1/2)

但是如果我改变行的顺序,它不会:

println(implicitly[Foo[D.type, B.type]].factor) // 7.5
println(implicitly[Foo[D.type, A.type]].factor) // does not compile

有很多或多或少相同的错误消息,例如

test.this.Base.transitiveFoo is not a valid implicit value for test.Foo[test.C.type,test.A.type] because: hasMatchingSymbol reported error: diverging implicit expansion for type test.Foo[test.C.type,Mid] starting with method transitiveFoo in object Base

diverging implicit expansion for type test.Foo[test.A.type,Mid] starting with method transitiveFoo in object Base

not enough arguments for method implicitly: (implicit e: test.Foo[test.D.type,test.A.type])test.Foo[test.D.type,test.A.type]. Unspecified value parameter e.

完整日志有点长。

给定 Foo[X, Y],获取 Foo[Y, X],并给定 Foo[X, Y] 和 Foo[Y, Z],获取 Foo[X, Z] 的正确方法是什么对于定义的 Foo 的任何组合? 像“Shapeless”“Lazy”这样的东西在这里会有帮助吗?提前致谢!

最佳答案

这个变体似乎有效:

  import shapeless.Lazy

  trait Foo[In <: Base, Out <: Base] {
    def factor : Double
  }

  sealed trait Base
  case object A extends Base
  case object B extends Base
  case object C extends Base
  case object D extends Base

  trait LowPriorityImplicits {
    implicit def symmetricFoo[In <: Base, Out <: Base](implicit foo : Lazy[Foo[In, Out]]) : Foo[Out, In] =
      new Foo[Out, In] {
        def factor : Double = 1.0 / foo.value.factor
      }
  }

  object Base extends LowPriorityImplicits {
    implicit def bFoo : Foo[B.type, A.type] =
      new Foo[B.type, A.type] {
        def factor : Double = 2.0
      }

    implicit def cFoo : Foo[C.type, A.type] =
      new Foo[C.type, A.type] {
        def factor : Double = 3.0
      }

    implicit def dFoo : Foo[D.type, C.type] =
      new Foo[D.type, C.type] {
        def factor : Double = 5.0
      }

    implicit def transitiveFoo[In <: Base, Out <: Base, Mid <: Base](implicit foo1 : Foo[In, Mid], foo2 : Foo[Mid, Out]) : Foo[In, Out] =
      new Foo[In, Out] {
        def factor : Double = foo1.factor * foo2.factor
      }
  }

  def main(args: Array[String]): Unit = {
    println(implicitly[Foo[D.type, B.type]].factor) 
    println(implicitly[Foo[D.type, A.type]].factor) 
  }

  7.5
  15.0

  def main(args: Array[String]): Unit = {
    println(implicitly[Foo[D.type, A.type]].factor)
    println(implicitly[Foo[D.type, B.type]].factor)
  }

  15.0
  7.5

以防万一我的 build.sbt:

scalaOrganization := "org.typelevel"
scalaVersion := "2.12.3-bin-typelevel-4"
libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.2"

关于scala - 发散的隐式扩张,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45279983/

相关文章:

haskell - 如何指定两个操作在类型类中交换?

haskell - 无法将预期类型 ‘r’ 与实际类型 ‘Horse’ 匹配

php - 如何检测 MySQL 中的隐式提交?

c# - 泛型类型 BoundedComponent 结构

mongodb - salat是否支持scala中Either类型的反/序列化?

scala - 将 paytm 回调 url 设置为本地主机

haskell - OCaml 中与 Haskell 类型类最接近的是什么?

c# - 转换 Dictionary<string,string> 时出现显式转换运算符错误

scala - 为什么这不会给出类型错误?

scala - 为什么我可以在 Scala Set 上使用单个参数调用 += ?