2015-09-24 1 views
6
[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]   ^

Я импортирую instances._; instances распространяется ExternalInstances1 и ExternalInstances1 распространяется ExternalInstances0. Из-за этого наследования я бы ожидал, что члены ExternalInstances1 выиграют более ExternalInstances0, вместо того чтобы уступать двусмысленность.Почему я получаю ошибку «двусмысленного implicits», несмотря на то, что у вас есть приоритеты?

Почему это происходит, и как я могу это исправить? Благодарю.

Источник находится в 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)) 
    } 
} 
+0

Вы не можете расставить приоритеты «implicits». Через наследование вы можете влиять только на порядок инициализации. Если вы хотите, чтобы неявное выражение 'ExternalInstances1' было * win *, вы должны переопределить 'implicits'' ExternalInstances0'. –

+3

@ Sascha Kolberg: Да, вы можете, и это обычная илама scala. См. Пример: http://stackoverflow.com/questions/1886953/is-there-a-way-to-control-which-implicit-conversion-will-be-the-default-used –

+1

Хм, мой плохой, didn Не знаю. Что ж, поскольку у ваших двух неявных преобразований есть несколько разные подписи, я бы предположил, что преобразование с более низким приоритетом по наследству может иметь лучший балл для его подписи, и из-за несчастного случая они оба получают одинаковый балл. Но я бы не хотел, чтобы этот человек отлаживал это;) –

ответ

3

Основываясь на комментарий Sasha Кольберг и 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 
} 
+0

Не могли бы вы просто обменять «ВнешниеInstances0» и «ExternalInstances1»? Кажется, что он работает нормально (как «неявно [QueryParamDecoder [String @@ Social]] неявно [QueryParamDecoder [String]]' компилируется правильно. Хотя я могу только сказать, что он компилируется отлично, а не то, что он делает то, что вы ожидаете –

+0

Правильно, их замена их разрешила бы конфликт, но придавала бы «ExternalInstances0» более высокий приоритет, я хочу, чтобы у него был более низкий приоритет. – arya