1

編集:それは私の問題を絞り込むために必要な情報が含まれていなかったとしてコンパイラの障害(時々)

最終改正は役に立たないと考えられました。したがって、ASTも含める必要があります。

以下は、ユーザー定義のスキーマに基づいてplay-jsonのjsonの解析と書き込みを可能にするライブラリです。今度は、問題を再現するクライアントコードの小片を書いてみましょう

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

私は、コンパイラが実行されることを巨大な疑いを持っているもの、ある程度のデータベースの列のためのScalaのslick申し出と同様に暗黙の型Sを推論する際にSの有界型がHasJsonSchema[T, S <: JsonSchema[T]]にあるため、すべてのコードの最後の行に示されているように、その特定の状況でのみ問題に変換されます。私が似たような状況を作り出し、タイプSが制限されていないと、私はこの問題を抱えないだろうと気づいた。有界型に依存しないようにコードをリファクタするあらゆる種類のソリューション、または暗黙の解決を単純に解決するソリューションが評価されます

+0

、なぜそれが'だけでなく 'JsonSchema [のS'を返すことがあります:あなたは、代わりに、より詳細な説明を型クラスを使用する必要がありますT] '? – SergGr

+0

@SergGr私はあなたの必要な情報で投稿を更新しました – shayan

+0

申し訳ありません、あなたはいくつかの問題を示すいくつかのコードを表示していますが、実際の問題の説明は避けてください。これは重大な可能性のある回答を制限し、また質問をもっと難しくします(答えるためにはコードを読まなければなりません)。また、 'root'や' reaplies'や 'postedBy'のようなものはどこにも言及されておらず、本当に何かを推測するのが難しいので、あなたの例は全く関係していないようです。だからもう一度、コードではなくロジックの面で解決しようとしている問題を記述してください。 – SergGr

答えて