What does the word “Action” do in a Scala function

2020-05-19 07:53发布

I am developing Play application and I've just started with Scala. I see that there is this word Action after the equals sign in the function below and before curly brace.

def index = Action {
  Ok(views.html.index("Hi there"))
}

What does this code do? I've seen it used with def index = { but not with the word before the curly brace.

I would assume that the name of the function is index. But I do not know what the word Action does in this situation.

3条回答
闹够了就滚
2楼-- · 2020-05-19 08:16

This word is a part of Play Framework, and it's an object, which has method apply(block: ⇒ Result), so your code is actually:

def index: Action[AnyContent] = Action.apply({
  Ok.apply(views.html.index("Hi there"))
})

Your index method returns an instance of the class Action[AnyContent].

By the way, you're passing a block of code {Ok(...)} to apply method, which (block of code) is actually acts as anonymous function here, because the required type for apply's input is not just Result but ⇒ Result, which means that it takes an anonymous function with no input parameters, which returns Result. So, your Ok-block will be executed when container, received your instance of class Action (from index method), decided to execute this block. Which simply means that you're just describing an action here - not executing - it will be actually executed when Play received your request - and find binding to your action inside routing file.

Also, you don't have to use def here as you always return same action - val or lazy val is usually enough. You will need a def only if you actually want to pass some parameter from routing table (for instance):

GET   /clients/:id          controllers.SomeController.index(id: Long)

def index(id: Long) = Action { ... } // new action generated for every new request here

Another possible approach is to choose Action, based on parameter:

def index(id: Long) = {
   if (id == 0) Action {...} else Action{...}
}

But uasually you can use routing table itself for that, which is better for decoupling. This example just shows that Action is nothing more than return value.


Update for @Kazuya

 val method1 = Action{...} //could be def too, no big difference here

 // this (code inside Action) gonna be called separately after "index" (if method2 is requested of course)
 // notice that it needs the whole request, so it (request) should be completely parsed at the time
 val method2 = Action{ req => // you can extract additional params from request
   val param1 = req.headers("header1")
   ...
 }

 //This is gonna be called first, notice that Play doesn't need the whole request body here, so it might not even be parsed on this stage
 def index(methodName: String) = methodName match {
   case "method1" => method1
   case "method2" => method2
 }

GWT/Scala.js use simillar approach for client-server interaction. This is just one possible solution to explain importance of the parameter "methodName" passed from routing table. So, action could be thought as a wrapper over function that in its turn represents a reference to OOP-method, which makes it useful for both REST and RPC purposes.

查看更多
▲ chillily
3楼-- · 2020-05-19 08:22

The other answers deal with your specific case. You asked about the general case, however, so I'll attempt to answer from that perspective.

First off, def is used to define a method, not a function (better to learn that difference now). But, you're right, index is the name of that method.

Now, unlike other languages you might be familiar with (e.g., C, Java), Scala lets you define methods with an expression (as suggested by the use of the assignment operator syntax, =). That is, everything after the = is an expression that will be evaluated to a value each time the method is invoked.

So, whereas in Java you have to say:

public int three() { return 3; }

In Scala, you can just say:

def three = 3

Of course, the expression is usually more complicated (as in your case). It could be a block of code, like you're more used to seeing, in which case the value is that of the last expression in the block:

def three = {
   val a = 1
   val b = 2
   a + b
}

Or it might involve a method invocation on some other object:

def three = Numbers.add(1, 2)

The latter is, in fact, exactly what's going on in your specific example, although it requires a bit more explanation to understand why. There are two bits of magic involved:

  1. If an object has an apply method, then you can treat the object as if it were a function. You can say, for example, Add(1, 2) when you really mean Add.apply(1,2) (assuming there's an Add object with an apply method, of course). And just to be clear, it doesn't have to be an object defined with the object keyword. Any object with a suitable apply method will do.
  2. If a method has a single by-name parameter (e.g., def ifWaterBoiling(fn: => Tea)), then you can invoke the method like ifWaterBoiling { makeTea }. The code in that block is evaluated lazily (and may not be evaluated at all). This would be equivalent to writing ifWaterBoiling({ makeTea }). The { makeTea } part just defines an expression that gets passed in, unevaluated, for the fn parameter.
查看更多
冷血范
4楼-- · 2020-05-19 08:23

Its the Action being called on with an expression block as argument. (The apply method is used under the hood).

Action.apply({
  Ok("Hello world")
})

A simple example (from here) is as follows (look at comments in code):

case class Logging[A](action: Action[A]) extends Action[A] {

  def apply(request: Request[A]): Result = {// apply method which is called on expression
    Logger.info("Calling action")
    action(request) // action being called on further with the request provided to Logging Action
  }

  lazy val parser = action.parser
}

Now you can use it to wrap any other action value:

def index = Logging { // Expression argument starts
  Action { // Action argument (goes under request)
    Ok("Hello World")
  }
}

Also, the case you mentioned for def index = { is actually returning Unit like: def index: Unit = {.

查看更多
登录 后发表回答