【问题标题】:why do I get an "ambiguous implicits" error despite having prioritized implicits?尽管优先考虑隐含,为什么我会收到“模棱两可的隐含”错误?
【发布时间】:2015-09-24 06:01:39
【问题描述】:
[error] test.scala:31: ambiguous implicit values:
[error]  both method taggedQueryParamDecoder in trait ExternalInstances0 of type [A, T](implicit evidence$2: org.http4s.QueryParamDecoder[A])org.http4s.QueryParamDecoder[scalaz.@@[A,T]]
[error]  and method iiQueryParamDecoder in trait ExternalInstances1 of type [B](implicit ii: foo.InvariantInstances[B])org.http4s.QueryParamDecoder[B]
[error]  match expected type org.http4s.QueryParamDecoder[scalaz.@@[String,foo.tags.Social]]
[error]   implicitly[QueryParamDecoder[String @@ Social]]
[error]             ^

我导入instances._instances 扩展 ExternalInstances1ExternalInstances1 扩展 ExternalInstances0。由于这种继承,我希望ExternalInstances1 的成员能够胜过ExternalInstances0 的成员,而不是产生歧义。

为什么会发生这种情况,我该如何解决?谢谢。

来源在http://scastie.org/12233,转载如下:

/***
scalaVersion := "2.11.7"

libraryDependencies += "org.http4s" %% "http4s-core" % "0.10.0"

resolvers ++= Seq(
  "tpolecat" at "http://dl.bintray.com/tpolecat/maven",
  "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases",
  Resolver.sonatypeRepo("releases")
)

libraryDependencies += "org.tpolecat" %% "doobie-core" % "0.2.2"

libraryDependencies += "com.github.alexarchambault" %% "argonaut-shapeless_6.1" % "0.3.1"
*/

import java.time.LocalDate

import argonaut._, Argonaut._
import doobie.imports._
import doobie.util.composite.Composite
import tags._
import instances._
import org.http4s._

import scala.reflect.runtime.universe.TypeTag
import scalaz._

object Main extends App {
  implicitly[QueryParamDecoder[String @@ Social]]
  println("ok")
}

object tags {
  trait Social; val Social = Tag.of[Social]
}

object instances extends ExternalInstances1 {
  implicit val ssn: InvariantInstances[String @@ Social] =
    InvariantInstances { (raw: String) ⇒
      val digitsOnly = raw.filter(_.isDigit)
      require(digitsOnly.length == 9)
      Social(digitsOnly)
    }(Social.unwrap)
}

trait ExternalInstances1 extends ExternalInstances0 {
  implicit def iiCodecJson[B](implicit ii: InvariantInstances[B]): CodecJson[B] = ii.codecJson
  implicit def iiEncodeJson[B](implicit ii: InvariantInstances[B]): EncodeJson[B] = ii.encodeJson
  implicit def iiDecodeJson[B](implicit ii: InvariantInstances[B]): DecodeJson[B] = ii.decodeJson
  implicit def iiMeta[B](implicit ii: InvariantInstances[B]): Meta[B] = ii.meta
  implicit def iiQueryParamEncoder[B](implicit ii: InvariantInstances[B]): QueryParamEncoder[B] = ii.queryParamEncoder
  implicit def iiQueryParamDecoder[B](implicit ii: InvariantInstances[B]): QueryParamDecoder[B] = ii.queryParamDecoder
}

trait ExternalInstances0 {
  implicit def taggedEncodeJson[A, T](implicit A: EncodeJson[A]): EncodeJson[A @@ T] =
    A.contramap(Tag.of[T].unwrap)

  implicit def taggedDecodeJson[A, T](implicit A: DecodeJson[A]): DecodeJson[A @@ T] =
    A.map(Tag.of[T].apply)

  implicit def taggedComposite[A: Composite, T]: Composite[A @@ T] =
    Composite[A].xmap(a => Tag[A, T](a), Tag.unwrap(_))

  implicit def taggedQueryParamDecoder[A: QueryParamDecoder, T]: QueryParamDecoder[A @@ T] =
    QueryParamDecoder.decodeBy(Tag.of[T](_: A))

}

trait InvariantInstances[B] {
  def codecJson: CodecJson[B]
  def decodeJson: DecodeJson[B]
  def encodeJson: EncodeJson[B]
  def meta: Meta[B]
  def queryParamEncoder: QueryParamEncoder[B]
  def queryParamDecoder: QueryParamDecoder[B]
}

object InvariantInstances {
  def apply[A: EncodeJson: DecodeJson: Meta: QueryParamDecoder: QueryParamEncoder, B: TypeTag](f: A ⇒ B)(g: B ⇒ A) =
    new InvariantInstances[B] {
      def codecJson: CodecJson[B] =
        CodecJson.derived[B](encodeJson, decodeJson)

      def decodeJson: DecodeJson[B] =
        implicitly[DecodeJson[A]].map(f)

      def encodeJson: EncodeJson[B] =
        implicitly[EncodeJson[A]].contramap(g)

      def meta: Meta[B] =
        implicitly[Meta[A]].xmap(f, g)

      def queryParamDecoder: QueryParamDecoder[B] =
        CovariantInstances.queryParamDecoder(f)

      def queryParamEncoder: QueryParamEncoder[B] =
        ContravariantInstances.queryParamEncoder(g)
    }
}

object CovariantInstances {
  def queryParamDecoder[A, B](f: A ⇒ B)(implicit A: QueryParamDecoder[A],
                                        B: reflect.runtime.universe.TypeTag[B]): QueryParamDecoder[B] =
    new QueryParamDecoder[B] {
      import scalaz.Validation.FlatMap._ // suppress deprecation warning

      def decode(value: QueryParameterValue): ValidationNel[ParseFailure, B] =
        A.decode(value).flatMap(
          a ⇒ Validation.fromTryCatchNonFatal(f(a)).leftMap(t =>
            ParseFailure(s"Query decoding ${B.tpe.typeSymbol} failed", t.getMessage)
          ).toValidationNel
        )
    }
}

object ContravariantInstances {
  def queryParamEncoder[A, B](g: B ⇒ A)(implicit A: QueryParamEncoder[A]): QueryParamEncoder[B] =
    new QueryParamEncoder[B] {
      def encode(value: B): QueryParameterValue = A.encode(g(value))
    }
}

【问题讨论】:

  • 您不能优先考虑“隐含”。通过继承,您只能影响初始化顺序。如果您希望 'ExternalInstances1' 的隐式 win 比您应该覆盖 ExternalInstances0implicits
  • @Sascha Kolberg:是的,你可以,这是一个常见的 scala 习惯用法。通过示例查看:stackoverflow.com/questions/1886953/…
  • 嗯,我的错,不知道。好吧,由于您的两个隐式转换的签名略有不同,我猜想通过继承具有较低优先级的转换可能具有更好的签名分数,并且由于意外事故,它们最终都得到相同的分数。但我不想成为调试这个的人;)

标签: scala implicit


【解决方案1】:

根据 Sasha Kolberg 的评论和 http://eed3si9n.com/revisiting-implicits-without-import-tax,我想我在 ExternalInstances0(+1 分)中有一个“更具体”的定义,在 ExternalInstances1(+1 分)中有一个“更高优先级”的定义,导致平局和模棱两可。

我的解决方法是在ExternalInstances1 中添加一个“更具体、更高优先级”的定义(+2 分?)以打破平局,即使它是重复代码,这基本上是样板文件。

我很想知道更好的解决方案。谢谢!

trait ExternalInstances1 extends ExternalInstances0 {
  implicit def iiCodecJson[B](implicit ii: InvariantInstances[B]): CodecJson[B] = ii.codecJson
  implicit def iiEncodeJson[B](implicit ii: InvariantInstances[B]): EncodeJson[B] = ii.encodeJson
  implicit def iiDecodeJson[B](implicit ii: InvariantInstances[B]): DecodeJson[B] = ii.decodeJson
  implicit def iiMeta[B](implicit ii: InvariantInstances[B]): Meta[B] = ii.meta
  implicit def iiQueryParamEncoder[B](implicit ii: InvariantInstances[B]): QueryParamEncoder[B] = ii.queryParamEncoder
  implicit def iiQueryParamDecoder[B](implicit ii: InvariantInstances[B]): QueryParamDecoder[B] = ii.queryParamDecoder

  implicit def iiCodecJsonT[B,T](implicit ii: InvariantInstances[B @@ T]): CodecJson[B @@ T] = ii.codecJson
  implicit def iiEncodeJsonT[B,T](implicit ii: InvariantInstances[B @@ T]): EncodeJson[B @@ T] = ii.encodeJson
  implicit def iiDecodeJsonT[B,T](implicit ii: InvariantInstances[B @@ T]): DecodeJson[B @@ T] = ii.decodeJson
  implicit def iiMetaT[B,T](implicit ii: InvariantInstances[B @@ T]): Meta[B @@ T] = ii.meta
  implicit def iiQueryParamEncoderT[B,T](implicit ii: InvariantInstances[B @@ T]): QueryParamEncoder[B @@ T] = ii.queryParamEncoder
  implicit def iiQueryParamDecoderT[B,T](implicit ii: InvariantInstances[B @@ T]): QueryParamDecoder[B @@ T] = ii.queryParamDecoder
}

【讨论】:

  • 你不能交换ExternalInstances0ExternalInstances1吗?似乎工作正常(implicitly[QueryParamDecoder[String @@ Social]] implicitly[QueryParamDecoder[String]] 编译正确。虽然我只能说它编译得很好,而不是你期望的那样。
  • 对,交换它们可以解决冲突,但给ExternalInstances0更高的优先级;我希望它的优先级较低。
猜你喜欢
  • 1970-01-01
  • 2023-03-24
  • 1970-01-01
  • 1970-01-01
  • 2018-12-23
  • 1970-01-01
  • 1970-01-01
  • 2019-08-23
  • 1970-01-01
相关资源
最近更新 更多