I have the following Scala/Play! code:
case class Tweet(from: String, text: String)
implicit val tweetReads = (
(JsPath \ "from_user_name").read[String] ~
(JsPath \ "text").read[String]) (Tweet.apply _)
I have several questions regarding the syntax and meaning of the above code:
- On what class/object is the
~
method invoked on?
- What is the class/type of the argument to
Tweet.apply
?
edit 1: full source code:
package models
import play.api.libs.json._
import play.api.libs.json.util._
import play.api.libs.json.Reads._
import play.api.libs.json.Writes._
import play.api.libs.functional.syntax._
case class Tweet(from: String, text: String)
object Tweet {
implicit val tweetReads = (
(JsPath \ "from_user_name").read[String] ~
(JsPath \ "text").read[String])(Tweet.apply _)
implicit val tweetWrites = (
(JsPath \ "from").write[String] ~
(JsPath \ "text").write[String])(unlift(Tweet.unapply))
}
Step by step:
Method \
on object JsPath
val path1: JsPath = JsPath \ "from_user_name"
val path2: JsPath = JsPath \ "text"
Method read
on object of type JsPath
val reads1: Reads[String] = path1.read[String]
val reads2: Reads[String] = path2.read[String]
There is no method ~
in Reads
, but there is such method in FunctionalBuilderOps
and there is an implicit conversion from M[T]
to FunctionalBuilderOps[M[_], T]
in play.api.libs.functional.syntax
- toFunctionalBuilderOps
.
val reads1FunctionalBuilderOps: FunctionalBuilderOps[Reads, String] =
toFunctionalBuilderOps(reads1)
val canBuild2: CanBuild2[String, String] = reads1FunctionalBuilderOps.~(reads2)
Tweet.apply _
is a scala syntax to create a FunctionN
using method with N
arguments:
val func: (String, String) => Tweet = Tweet.apply _
There is an apply
method in CanBuild2[A, B]
. It accepts (A, B) => C
and returns Reads[C]
(in this case):
implicit val tweetReads: Reads[Tweet] = canBuild2.apply(func)
Actually there are also implicit parameters in JsPath#read
, toFunctionalBuilderOps
and CanBuild2#apply
methods. With that parameters:
val reads1: Reads[String] = path1.read[String](Reads.StringReads)
...
val reads1FunctionalBuilderOps: FunctionalBuilderOps[Reads, String] =
toFunctionalBuilderOps(reads1)(
functionalCanBuildApplicative(
Reads.applicative(JsResult.applicativeJsResult)))
...
implicit val tweetReads: Reads[Tweet] =
canBuild2.apply(func)(functorReads)
I would just add that this is called applicative syntax (for applicative functors). It can be seen for example in scala parser combinator library
If you want to know how to use it with json try to read this. Another example of applicative functors in play framework is form handling. Or you can try to read about scalaz Validation.