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

Posted

技术标签:

【中文标题】编译器无法解析带边界的隐式类型(有时)【英文标题】:compiler failure to resolve Implicit types with bounds (sometimes) 【发布时间】:2018-05-22 06:24:41 【问题描述】:

编辑

最后一次修订被认为没有帮助,因为它没有包含有助于缩小我的问题的必要信息。因此还需要包含 AST。

下面是一个完整的库,它允许基于用户定义的模式解析和写入play-json 的 json;在某种程度上类似于 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)

现在让我们编写一小段重现该问题的客户端代码:

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 时会因为S inHasJsonSchema[T, S &lt;: JsonSchema[T]] 的有界类型而遇到问题。到目前为止,仅在最后一行所示的特定情况下代码。作为一个调试尝试,我创建了一个类似的情况,并意识到如果类型 S 不受限制,我就不会遇到这个问题。任何一种重构代码使其不依赖于有界类型或仅解决隐式解析的解决方案都值得赞赏

【问题讨论】:

您能否提供更多关于repath 的作用以及为什么它必须返回S 而不仅仅是JsonSchema[T] 的详细信息? @SergGr 我用您所需的信息更新了帖子 对不起,您正在显示一些代码来显示一些问题,但您避免描述实际问题是什么。这严重限制了可能的答案,也使问题变得更加困难(因为要回答你必须阅读代码)。此外,您的示例似乎完全不相关,因为之前没有提到诸如 rootreapliespostedBy 之类的东西,因此很难猜测它们到底是什么。再次,请您用逻辑而不是代码来描述您要解决的问题吗? @SergGr 我非常感谢您到目前为止一直在努力帮助我。我修改了问题并尽我所能帮助缩小问题范围。如果你需要运行它。只需复制和粘贴。 【参考方案1】:

子类型无法实现您想要实现的目标。您应该改用类型类,更深入的解释:

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

【讨论】:

以上是关于编译器无法解析带边界的隐式类型(有时)的主要内容,如果未能解决你的问题,请参考以下文章

如何确定两个优先于重载解析的隐式转换序列?

仅对lambda表达式的隐式类型推断?为什么?困惑!

隐式方法参数解析? Scala编译器?

用户定义的隐式转换运算符和重载解析

无法在 varbinary(max) 中插入空值并出现错误:不允许从数据类型 nvarchar 到 varbinary(max) 的隐式转换

java 中使用jmx对tomcat实时监控默认的构造方法无法处理的异常类型的隐式超级构造函数抛出异常