2014-08-29 3 views
11

Зачем играть-Reads черта JSON не объявлен коварианты:Почему Чтение не объявлено ковариантным?

trait Reads[+A] 

Связанные сутью: https://gist.github.com/robertberry/9410272

Мешает ковариация/контрвариантность с implicits?

Или необязательно, как написать Reads экземпляр для запечатанных признаков? https://gist.github.com/phadej/c60912802dc494c3212b

ответ

1

Это может быть ковариантным, особенно если вы рассматриваете Reads[A] как более богатую форму JsValue => A.

Но .... implicitness.

Reads[A] не только способ преобразования из JsValue в A, это путь.

Так что, если мы имели

sealed trait Foo 
case class Bar(a: Int) 
case class Baz(b: Int) 

Если вы определили Reads[Bar], вы бы также (с ковариации) имеют Reads[Foo].

Это может быть немного странно.

object Foo { 
    implicit reads: Reads[Foo] = 
    implicitly[Reads[Bar]].orElse[implicitly[Reads[Baz]]] 
} 
object Bar { 
    implicit reads = Json.reads[Bar] // {"a":0} 
} 
object Baz { 
    implicit reads = Json.reads[Baz] // {"b":0} 

    def blah(jsValue: JsValue): Foo = jsValue.as[Foo] 
} 
object Main { 
    def blah(jsValue: JsValue): Foo = jsValue.as[Foo] 
} 

Что происходит в Baz.blah и Main.blah? Первый использует Baz.reads, а последний использует Foo.reads из-за (сложного) неявного порядка разрешения.

Это краевой кейс, и я все еще думаю, что вы можете сделать хороший аргумент для ковариации, но он показывает разницу между «вещью, возможно, разобрать JSON на Foo» и «вещь, которая может анализировать JSON во всех возможно Foo ".

+0

Спасибо, это не так, как я думал. Можете ли вы прокомментировать суть. Как вы думаете, следует ли писать 'читает [Child] .map (x => x)' или 'читает [Child] .as [Reads [Parent]]', может ли что-то не так случилось в последнем? – phadej

+0

@phadej, вы имеете в виду '.asInstanceOf'? В любом случае, я не думаю, что с расширением этого типа есть что-то плохое. Единственная проблема заключается в том, если это делается для implicits автоматически. –

8

Предположим, что Reads были ковариантными. У меня есть простая иерархия типов:

sealed trait Foo { def name: String } 
case class Bar(name: String, i: Int) extends Foo 
case class Baz(name: String, c: Char) extends Foo 

И в Reads экземпляре для одного из классов дел:

import play.api.libs.functional.syntax._ 
import play.api.libs.json._ 

implicit val readsBar: Reads[Bar] = (
    (__ \ 'name).read[String] and (__ \ 'i).read[Int] 
)(Bar.apply _) 

Bar <: Foo Но, так Reads[Bar] <: Reads[Foo], и это не имеет никакого смысла, я не имею сказал что-нибудь о том, как декодировать Baz, поэтому у меня явно нет Reads[Foo].

Лучшим вопросом может быть, почему Reads не является контравариантным.

+0

Но если у меня есть неявный 'readsBar: Reads [Bar]' и 'readsBaz: Reads [Baz]': тогда 'readsFoo = readsBar илиElse readsBaz' не работает. Однако 'readsFoo = readsBar.map (x => x) илиElse readsBaz.map (x => x)' в порядке. Здесь я вижу ковариантные карты. – phadej

+0

Это все еще беспокоит меня, я написал небольшую сущность, чтобы, надеюсь, прояснить проблему: https://gist.github.com/phadej/c60912802dc494c3212b – phadej

+0

Мне этого объяснения не достаточно. У вас все еще есть способ перевода JSON в 'Foo', поэтому в этом случае' Reads [Bar] '* is * a' Reads [Foo] '. –