编译器无法解析带边界的隐式类型(有时)
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 <: JsonSchema[T]]
的有界类型而遇到问题。到目前为止,仅在最后一行所示的特定情况下代码。作为一个调试尝试,我创建了一个类似的情况,并意识到如果类型 S
不受限制,我就不会遇到这个问题。任何一种重构代码使其不依赖于有界类型或仅解决隐式解析的解决方案都值得赞赏
【问题讨论】:
您能否提供更多关于repath
的作用以及为什么它必须返回S
而不仅仅是JsonSchema[T]
的详细信息?
@SergGr 我用您所需的信息更新了帖子
对不起,您正在显示一些代码来显示一些问题,但您避免描述实际问题是什么。这严重限制了可能的答案,也使问题变得更加困难(因为要回答你必须阅读代码)。此外,您的示例似乎完全不相关,因为之前没有提到诸如 root
或 reaplies
或 postedBy
之类的东西,因此很难猜测它们到底是什么。再次,请您用逻辑而不是代码来描述您要解决的问题吗?
@SergGr 我非常感谢您到目前为止一直在努力帮助我。我修改了问题并尽我所能帮助缩小问题范围。如果你需要运行它。只需复制和粘贴。
【参考方案1】:
子类型无法实现您想要实现的目标。您应该改用类型类,更深入的解释:
http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
【讨论】:
以上是关于编译器无法解析带边界的隐式类型(有时)的主要内容,如果未能解决你的问题,请参考以下文章
无法在 varbinary(max) 中插入空值并出现错误:不允许从数据类型 nvarchar 到 varbinary(max) 的隐式转换