Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Syntax and meaning of a Scala/Play! code sample

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))
}
like image 493
balteo Avatar asked Dec 25 '22 15:12

balteo


2 Answers

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)
like image 66
senia Avatar answered Jan 05 '23 00:01

senia


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.

like image 23
Martin Kolinek Avatar answered Jan 05 '23 01:01

Martin Kolinek