简体   繁体   English

编译器无法解析带边界的隐式类型(有时)

[英]compiler failure to resolve Implicit types with bounds (sometimes)

Edit : 编辑

Last revision was deemed unhelpful as it did not contain necessary information that help narrow down my issue. 上次修订被认为没有帮助,因为它不包含有助于缩小问题范围的必要信息。 hence the need to also include the AST. 因此,还需要包括AST。

Below is a library in its entirety that allows parsing and writing of play-json 's json based on user defined schema; 下面是一个完整的库,它允许基于用户定义的模式来解析和写入play-json的json; Similar to what Scala's slick offers for database columns to some extent: 在某种程度上类似于Scala的slick为数据库列提供的内容:

import scala.language.higherKinds
import play.api.libs.functional.syntax._
import play.api.libs.json._

import scala.language.{higherKinds, implicitConversions}

type PathNodes = List[PathNode]

sealed trait Field[A] {

  def pathNodes: PathNodes

  def jsPath: JsPath = JsPath(pathNodes)

  def relativePath: JsPath = JsPath(List(pathNodes.last))

  def format: Format[A]

  def nestedFormatter(path: JsPath): OFormat[A]

  def nestedFormat: OFormat[A] = nestedFormatter(relativePath)
}

case class PlainField[A: Format](prefix: PathNodes) extends Field[A] {

  override def pathNodes: PathNodes = prefix

  def format: Format[A] = implicitly[Format[A]]

  override def nestedFormatter(path: JsPath): OFormat[A] = path.format(format)
}

abstract class JsonSchema[T](val _prefix: PathNodes) extends Field[T] with SchemaExtensionMethods {

  override def pathNodes: PathNodes = _prefix

  def format: OFormat[T]

  protected def plain[A: Format](name: String): PlainField[A] = PlainField[A](_prefix :+ KeyPathNode(name))

  protected def nested[N](name: String, factory: PathNodes => N): N = factory(_prefix :+ KeyPathNode(name))

  protected def nested[B, G <: JsonSchema[B]](name: String)(implicit sm: HasJsonSchema[B, G]): G = sm.apply(_prefix :+ KeyPathNode(name))

  override def nestedFormatter(path: JsPath): OFormat[T] = path.format(format)
}

case class Optional[F, A](field: F)(implicit ev: F <:< Field[A]) extends Field[Option[A]] {

  override def pathNodes: PathNodes = field.pathNodes

  override def format: Format[Option[A]] = {
    implicit val writes: Writes[Option[A]] = JsPath.writeNullable(field.format)
    implicit val reads: Reads[Option[A]] = JsPath.readNullable(field.format)
    implicitly[Format[Option[A]]]
  }

  def map[G, B](f: F => G)(implicit ev: G <:< Field[B]): Optional[G, B] = new Optional[G, B](f(field))

  def flatMap[G <: Field[B], B](f: F => Optional[G, B]): Optional[G, B] = f(field)

  override def nestedFormatter(path: JsPath): OFormat[Option[A]] = path.formatNullable(field.format)
}

case class Collection[F, A](field: F)(implicit ev: F <:< Field[A], repath: Repath[F]) extends Field[Seq[A]] {
  override def pathNodes: PathNodes = field.pathNodes

  override def format: Format[Seq[A]] = {
    implicit val writes: Writes[Seq[A]] = Writes.seq(field.format)
    implicit val reads: Reads[Seq[A]] = Reads.seq(field.format)
    implicitly[Format[Seq[A]]]
  }

  def apply(idx: Int): F = implicitly[Repath[F]].apply(field, IdxPathNode(idx))

  override def nestedFormatter(path: JsPath): OFormat[Seq[A]] = path.format(format)
}

class FormatExtensionMethods[T](val arg: T) {
  def <>[A, B, Fun](apply: Fun, unapply: B => Option[A])(implicit jss: JsonShape[A, B, T, Fun]): OFormat[B] = jss.format(arg, apply, unapply andThen (_.get))
}

class FieldExtensionMethods[F](val field: F) {
  def optional[A](implicit ev: F <:< Field[A]): Optional[F, A] = new Optional[F, A](field)

  def sequence[A](implicit ev: F <:< Field[A], repath: Repath[F]): Collection[F, A] = new Collection[F, A](field)
}

trait SchemaExtensionMethods {
  implicit def formatExtensionMethods[M](t: M): FormatExtensionMethods[M] = new FormatExtensionMethods[M](t)

  implicit def fieldExtensionMethods[M, A](t: M): FieldExtensionMethods[M] = new FieldExtensionMethods[M](t)
}

trait Repath[F] {
  def apply(f: F, node: PathNode): F
}

object Repath {
  implicit def plain[T]: Repath[PlainField[T]] = new Repath[PlainField[T]] {
    override def apply(t: PlainField[T], node: PathNode): PlainField[T] =
      PlainField[T](t.pathNodes :+ node)(t.format)
  }

  implicit def schema[S <: JsonSchema[_]](implicit sm: HasJsonSchema[_, S]): Repath[S] = new Repath[S] {
    override def apply(t: S, node: PathNode): S =
      sm.apply(t.pathNodes :+ node)
  }

  implicit def option[F <: Field[T] : Repath, T]: Repath[Optional[F, T]] = new Repath[Optional[F, T]] {
    override def apply(t: Optional[F, T], node: PathNode): Optional[F, T] =
      new Optional[F, T](implicitly[Repath[F]].apply(t.field, node))
  }

  implicit def sequence[F <: Field[T] : Repath, T]: Repath[Collection[F, T]] = new Repath[Collection[F, T]] {
    override def apply(t: Collection[F, T], node: PathNode): Collection[F, T] =
      new Collection[F, T](implicitly[Repath[F]].apply(t.field, node))
  }
}

trait JsonShape[A, B, -T, Func] {
  def format(t: T, apply: Func, unapply: B => A): OFormat[B]
}

object JsonShape {
  type F[T] = Field[T]

  implicit def cc1[A, B]: JsonShape[A, B, F[A], (A) => B] = (t: F[A], apply: (A) => B, unapply: B => A) => {
    val name = t.pathNodes.last.asInstanceOf[KeyPathNode].key
    OFormat[B](
      Reads[B](jsv => (jsv \ name).validate[A](t.format).map(apply)),
      OWrites[B](b => JsObject(Map(name -> Json.toJson(unapply(b))(t.format))))
    )
  }

  implicit def cc2[T1, T2, B]: JsonShape[(T1, T2), B, (F[T1], F[T2]), (T1, T2) => B] = (t: (F[T1], F[T2]), apply: (T1, T2) => B, unapply: B => (T1, T2)) => {
    (
      t._1.nestedFormat and
        t._2.nestedFormat
      ) (apply, unapply)
  }

  implicit def cc3[T1, T2, T3, B]: JsonShape[(T1, T2, T3), B, (F[T1], F[T2], F[T3]), (T1, T2, T3) => B] = (t: (F[T1], F[T2], F[T3]), apply: (T1, T2, T3) => B, unapply: B => (T1, T2, T3)) => {
    (
      t._1.nestedFormat and
        t._2.nestedFormat and
        t._3.nestedFormat
      ) (apply, unapply)
  }

  //this goes up to 22

}

abstract class HasJsonSchema[T, +S <: JsonSchema[T]](val apply: PathNodes => S) extends OFormat[T] {
  val root: S = apply(Nil)

  def format: OFormat[T] = root.format

  def writes(o: T): JsObject = root.format.writes(o)

  def reads(json: JsValue): JsResult[T] = root.format.reads(json)
}

Now let's write a small piece of client code that reproduce the issue: 现在,让我们写一小段客户端代码来重现该问题:

case class MessageSchema(prefix: PathNodes) extends JsonSchema[Message](prefix) {

  def underlying = plain[String]("underlying")
  //def underlying = plain[String]("underlying").optional if I wanted the field to be Option[String]
  //def underlying = plain[String]("underlying").sequence if I wanted the field to be Seq[String]
  override def format = underlying <> (Message.apply _, Message.unapply)

}

case class Message(underlying: String)

object Message {

  implicit object sm extends HasJsonSchema[Message, MessageSchema](MessageSchema.apply)

}

case class LanguageTaggedSchema[T, S <: JsonSchema[T]](prefix: PathNodes)(implicit evT: HasJsonSchema[T, S]) extends JsonSchema[LanguageTagged[T]](prefix) {
  def lang = plain[String]("lang")

  def data: S = nested("data")(evT)

  def format = (lang, data) <> (LanguageTagged.apply[T] _, LanguageTagged.unapply[T])
}

case class LanguageTagged[T](lang: String, data: T)

object LanguageTagged {

  implicit def schemaMapper[T, S <: JsonSchema[T]](implicit ev: HasJsonSchema[T, S]): HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]] =
    new HasJsonSchema[LanguageTagged[T], LanguageTaggedSchema[T, S]](LanguageTaggedSchema.apply[T, S]) {}
}

def toJson[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): JsValue = Json.toJson(a)(ev.format)

toJson(Message("hi")) //Ok!
toJson(LanguageTagged("en", Message("hi"))) //Ok!
//or simply write
Json.toJson(LanguageTagged("en", Message("hi")))

//and if i wanted to traverse a json path i would do:
val schema = implicitly[HasJsonSchema[LanguageTagged[Message],LanguageTaggedSchema[Message,MessageSchema]]].root
schema.data.underlying.jsPath
//prints: res2: play.api.libs.json.JsPath = /data/underlying

//Now to where the problem starts:

def getSchema[T, S <: JsonSchema[T]](a: T)(implicit ev: HasJsonSchema[T, S]): S = ev.root

getSchema(Message("hi")) //Ok!
getSchema(LanguageTagged("en", Message("hi"))) //Not Ok but why?
//Error:(211, 11) could not find implicit value for
//parameter ev: A$A6.this.HasJsonSchema[A$A6.this.LanguageTagged[A$A6.this.Message],S]
//getSchema(LanguageTagged("en", Message("hi")));//
//^

I have a huge suspicion that the compiler runs into issues because of the bounded type of S in HasJsonSchema[T, S <: JsonSchema[T]] when infering the implicit type S. and so far only in that specific situation as shown on the last line of all the code. 我非常怀疑编译器会遇到问题,因为在推断隐式类型S时, HasJsonSchema[T, S <: JsonSchema[T]]S是有限类型,到目前为止,仅在特定情况下,如所有代码的最后一行。 as a dubugging attempt I created a similar situation and realized that if the type S was not bounded I wouldn't have this issue. 作为调试的尝试,我遇到了类似的情况,并且意识到,如果类型S没有限制,我就不会遇到这个问题。 Any sort of solution that refactors the code such that it doesn't depend on bounded types or one that simply solves the implicit resolution is appreciated 可以重构代码而不依赖于有界类型或仅解决隐式分辨率的任何解决方案

What You're trying to achieve cannot be done with subtyping. 您要实现的目标无法通过子类型化完成。 You should use type-classes instead, a more in-depth explanation: 您应该改用类型类,更深入的解释:

http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM