编译器无法解析带边界的隐式类型(有时) [英] compiler failure to resolve Implicit types with bounds (sometimes)

查看:83
本文介绍了编译器无法解析带边界的隐式类型(有时)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

修改:

上次修订被认为无济于事,因为它不包含有助于缩小问题范围的必要信息.因此也需要包含AST.

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.

下面是一个完整的库,它允许基于用户定义的模式来解析和写入play-json的json;在某种程度上类似于Scala的slick为数据库列提供的内容:

Below is a library in its entirety that allows parsing and writing of play-json's json based on user defined schema; Similar to what Scala's slick offers for database columns to some extent:

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")));//
//^

我非常怀疑编译器会遇到问题,因为在推断隐式类型S时,由于HasJsonSchema[T, S <: JsonSchema[T]]中的SS的有界类型.代码.作为调试的尝试,我遇到了类似的情况,并且意识到如果S类型不受限制,我就不会遇到这个问题.可以对代码进行重构以使其不依赖于有界类型或仅解决隐式分辨率的任何一种解决方案

I have a huge suspicion that the compiler runs into issues because of the bounded type of S inHasJsonSchema[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. 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. 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

这篇关于编译器无法解析带边界的隐式类型(有时)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆