Zuriar
Zuriar

Reputation: 11734

How should I read this piece of Scala (Play) code?

I am new to Scala, and am learning it by going over some Play code. I have had a good read of the major concepts of Scala and am comfortable with functional programming having done some Haskell and ML.

I am really struggling to read this code, at the level of the syntax and the programming paradigms alone. I understand what the code is supposed to do, but not how it does it because I can't figure out the syntax.

  // -- Home page
def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref) { implicit request =>
for {
  someDocuments <- ctx.api.forms("everything").ref(ctx.ref).submit()
} yield {
    Ok(views.html.index(someDocuments))
  }
}

(Prismic is an API separate to Play and is not really that relevant). How would I describe this function (or is it a method??) to another developer over the phone: in other words, using English. For example in this code:

def add(a: Int, b: Int): Int = a + b

I would say "add is a function which takes two integers, adds them together and returns the result as another integer".

In the Play code above I don't even know how to describe it after getting to "index is a function which takes an Option of a String and returns an Action of type AnyContent by ....."

The bit after the '=' and then the curly braces and the '=>' scare me! How do I read them? And is the functional or OO?

Thanks for your assistance

Upvotes: 0

Views: 175

Answers (3)

Daniel C. Sobral
Daniel C. Sobral

Reputation: 297155

Let's reduce it to this:

def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)(function)

That's better, isn't it? index is a function from Option of String to Action of AnyContent (one word), which calls the action method of the object Prismic passing two curried parameters: ref, the parameter that index received, and a function (to be described).

So let's break down the anonymous function:

{ implicit request =>
  for {
    someDocuments <- ctx.api.forms("everything").ref(ctx.ref).submit()
  } yield {
    Ok(views.html.index(someDocuments))
  }
}

First, it uses {} instead of () because Scala allows one to drop () as parameter delimiter if it's a single parameter (there are two parameter lists, but each has a single parameter), and that parameter is enclosed in {}.

So, what about {}? Well, it's an expression that contains declarations and statements, with semi-colon inference on new lines, whose value is that of the last statement. That is, the value of these two expressions is the same, 3:

{ 1; 2; 3 }
{
  1
  2
  3
}

It's a syntactic convention to use {} when passing a function that extends for more than one line, even if, as in this case, that function could have been passed with just parenthesis.

The next thing confusing is the implicit request =>, Let's pick something simpler:

x => x * 2

That's pretty easy, right? It takes one parameter, x, and returns x * 2. In our case, it is the same thing: the function takes one parameter, request, and returns this:

  for (someDocuments <- somethingSomething())
  yield Ok(views.html.index(someDocuments))

That is, it calls some methods, iterate over the result, and map those results into a new value. This is a close equivalent to Haskell's do notation. You can rewrite it like below (I'm breaking it down into multiple lines for readability):

  ctx
    .api
    .forms("everything")
    .ref(ctx.ref)
    .submit()
    .map(someDocuments => Ok(views.html.index(someDocuments)))

So, back to our method definition, we have this:

def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)(
  implicit request =>
  ctx
    .api
    .forms("everything")
    .ref(ctx.ref)
    .submit()
    .map(someDocuments => Ok(views.html.index(someDocuments)))
)

The only remaining question here is what that implicit is about. Basically, it makes that parameter implicitly available through the scope of the function. Presumably, at least one of these method calls require an implicit parameter which is properly fielded by request. I could drop the implicit there an pass request explicitly, if I knew which of these methods require it, but since I don't, I'm skipping that.

An alternate way of writing it would be:

def index(ref: Option[String]): Action[AnyContent] = Prismic.action(ref)({
  request =>
  implicit val req = request
  ctx
    .api
    .forms("everything")
    .ref(ctx.ref)
    .submit()
    .map(someDocuments => Ok(views.html.index(someDocuments)))
})

Here I added {} back because I added a declaration to the body of the function, though I decided not to drop the parenthesis, which I could have.

Upvotes: 4

Yann Moisan
Yann Moisan

Reputation: 8281

  • Prismic.action is a method that takes 2 groups of arguments (a.k.a. currying).

  • The first is ref

  • The second is { implicit request => ...}, a function defined in a block of a code

more information on Action

Upvotes: 0

user908853
user908853

Reputation:

Something like this:

index is a function which takes an Option of a String and returns an Action of type AnyContent. It calls the method action that takes as a first argument an Option and as a second argument a method that assumes an implicit value request of type Request is in scope. This method uses a For-comprehension that calls the submit method which returns an Option or a Future and then in case its execution is successful, it yields the result Ok(...) that will be wrapped in the Action returned by the action method of Prismic.

Upvotes: 0

Related Questions