For this case class:
case class People(names: Set[Int])
Travis Brown explained how to create PeopleReads: Reads[People]
at this ans
Good question! The reason you can't use map
is because Writes
isn't a functor.
You can think of Writes[A]
as something kind of like A => JsValue
. But suppose I've got a A => JsValue
and a A => B
. Try to come up with some way of composing those functions to get a B => JsValue
—it's just not possible.
Reads[A]
, on the other hand, is kind of like JsValue => A
, and is a functor—it has a map
method that takes a A => B
, composes it with the Reads[A]
/ JsValue => A
, and returns a Reads[B]
/ JsValue => B
.
Writes
is, however, a contravariant functor, and luckily enough Play knows that. When F
is a contravariant functor, F[A]
has a method contramap[B](f: B => A)
instead of the usual map[B](f: A => B)
. So you can just write this:
case class People(names: Set[Int])
import play.api.libs.json._
import play.api.libs.functional.syntax._
implicit val PeopleWrites: Writes[People] =
(__ \ 'names).write[Set[Int]].contramap(_.names)
Here (__ \ 'names).write[Set[Int]]
is a Writes[Set[Int]]
and (_.names)
is a function People => Set[Int]
. Combining them with contramap
gives us a Writes[People]
.