2016-04-02 18 views
7

nicht sicher, dies ist ein Fehler, aber die folgende Demo nicht auf den letzten Fälle:Spray-json für Seq von Eithers andernfalls

import spray.json._ 
import DefaultJsonProtocol._ 

object SprayTest { 
    1.toJson 
    "".toJson 
    (Left(1): Either[Int, String]).toJson 
    (Right(""): Either[Int, String]).toJson 
    Seq(1).toJson 
    Seq("").toJson 
    Seq(Left(1), Right("")).toJson 
    Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))) 
} 

So die alle Bausteine ​​erscheinen zu arbeiten, aber die Zusammensetzung des Formats für Seq und Either schlägt fehl, auch wenn ich versuche, es zu löffeln.

ich die folgenden Fehler sehen:

[error] SprayTest.scala:11: Cannot find JsonWriter or JsonFormat type class for Seq[Product with Serializable with scala.util.Either[Int,String]] 
[error] Seq(Left(1), Right("")).toJson 
[error]       ^
[error] SprayTest.scala:12: type mismatch; 
[error] found : spray.json.DefaultJsonProtocol.JF[Either[Int,String]] 
[error]  (which expands to) spray.json.JsonFormat[Either[Int,String]] 
[error] required: spray.json.JsonFormat[Product with Serializable with scala.util.Either[Int,String]] 
[error] Note: Either[Int,String] >: Product with Serializable with scala.util.Either[Int,String] (and spray.json.DefaultJsonProtocol.JF[Either[Int,String]] <: spray.json.JsonFormat[Either[Int,String]]), but trait JsonFormat is invariant in type T. 
[error] You may wish to define T as -T instead. (SLS 4.5) 
[error] Seq(Left(1), Right("")).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat))) 

Jede Idee, was soll das?

Antwort

14

Dies ist einer der ärgerlichsten Dinge über Either -die Left und Right Bauer erstrecken sich beide Product und Serializable, aber Either selbst nicht, was schrecklich abgeleitete Typen führt:

scala> Seq(Left(1), Right("")) 
res0: Seq[Product with Serializable with scala.util.Either[Int,String]] = List(Left(1), Right()) 

Weil JsonFormat ist Invariant in seinem Typ Parameter, die Tatsache, dass Sie eine Instanz für A haben bedeutet nicht, dass Sie eine Instanz für Product with Serializable with A haben. In Ihrem Fall gibt es tatsächlich eine Instanz für Either[Int, String], aber der zusätzliche Fehler im abgeleiteten Typ bedeutet, dass der Compiler sie nicht finden kann.

Ähnliches passiert, wenn Sie nicht über eine Right in der Folge haben:

scala> val xs: Seq[Either[Int, String]] = Seq(Left(1), Right("")) 
xs: Seq[Either[Int,String]] = List(Left(1), Right()) 

scala> xs.toJson 
res1: spray.json.JsValue = [1,""] 

:

scala> Seq(Left(1), Left(2)).toJson 
<console>:18: error: Cannot find JsonWriter or JsonFormat type class for Seq[scala.util.Left[Int,Nothing]] 
     Seq(Left(1), Left(2)).toJson 
          ^

Sie beide Probleme eine Art, indem sie anstelle der Verwendung der abgeleiteten eine beheben In vielen Fällen ist dies kein Problem, da Sie Ihre Either Werte oft von Methoden erhalten, die explizit Either anstelle von Left und Right direkt in Weisen zurückgeben die zu diesem Problem führen.

Als eine Fußnote: Deshalb sollten Sie immer Ihre Wurzel versiegelte Eigenschaft (oder versiegelte Klasse) Product with Serializable erweitern, wenn Sie Ihre eigenen ADTs definieren. Wir wären alle viel besser dran, wenn die Standard-Bibliotheksdesigner diesem Rat gefolgt wären.

+0

Das behebt mein Beispiel hier, aber leider repariert es meinen echten Code nicht :(Aber sehr informativ, danke! – acjay

2

Ich denke, wenn Sie Typ Zuschreibung auf die Elemente der Seqs hinzufügen Es wird kompilieren:

Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson 
Seq(Left(1): Either[Int, String], Right(""): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)) 

Sie können es auch einen einzigen Typ Zuschreibung geben:

(Seq(Left(1), Right("")): Either[Int, String]).toJson 
(Seq(Left(1), Right("")): Either[Int, String]).toJson(seqFormat(eitherFormat(IntJsonFormat, StringJsonFormat)) 

denke ich, das Problem scalac versucht, die häufigsten kleinsten Obergrenzen zwischen den Elementen, die Sie Seq zur Verfügung stellen, zu bestimmen, um einen einzigen Typ abzuleiten (da Standardkollektionen homogene Datentypen für ihre Elemente benötigen) und leitet nicht ab, was Sie wollen, ohne es zu geben Hilfe. Wenn die Scala-Standardbibliothek erweitertes Produkt mit Serializable zur abstrakten Klasse hinzugefügt hätte Eine der beiden Definitionen würde dies nicht erfordern, aber da sowohl die Untertypen Right als auch Left Fallklassen sind (die implizit Product und Serializable erweitern), werden sie einbezogen im abgeleiteten Typ, der Probleme mit dem invarianten Typ verursacht, der für Spray erforderlich ist.

+0

sieht aus wie Travis mich dazu schlagen. :-) –