[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[[email protected]@[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[[email protected]@[String,foo.tags.Social]]
[error] implicitly[QueryParamDecoder[String @@ Social]]
[error] ^
Importazione instances._
; instances
estende ExternalInstances1
e ExternalInstances1
estende ExternalInstances0
. A causa di questa ereditarietà, mi aspetterei che i membri di ExternalInstances1
vincano oltre ExternalInstances0
, piuttosto che cedere a un'ambiguità.perché ottengo un errore "implicito ambiguo" nonostante abbia implicito priorità per gli impliciti?
Perché sta succedendo e come posso risolverlo? Grazie.
La fonte è a http://scastie.org/12233, di seguito riprodotto:
/***
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))
}
}
Non è possibile dare la priorità agli 'impliciti'. Attraverso l'ereditarietà puoi solo influenzare l'ordine di inizializzazione. Se si desidera l'implicito di 'ExternalInstances1' su * win * di quanto si dovrebbe sovrascrivere il 'implicits' di' ExternalInstances0'. –
@Sascha Kolberg: Sì, è possibile, e questo è un idioma di scala comune. Vedere per esempio: http://stackoverflow.com/questions/1886953/is-there-a---control-which-implicit-conversion-will-be-the-default-used –
Hm, my bad, didn lo so. Bene, dato che le tue due conversioni implicite hanno firme leggermente diverse, immagino che la conversione con priorità più bassa per eredità possa avere un punteggio migliore per la sua firma e, a causa di uno strano incidente, entrambi finiscono con lo stesso punteggio. Ma non vorrei essere la persona che esegue il debug di questo;) –