Wow. Thanks for that detailed explanation! That is really great stuff
to know.
--
--
--
--
> Now because of the current limitations with type erasureWhich problem are you referring to?
Yes this is the only possible solution that would allow for:Action {Ok("Hello!")}And:Action {somethingInTheFuture.map { x =>Ok("Hello " + x)}}Now we can also solve this at the naming level as proposed by James. I think that Action.mapM would be fine.
On Tue, Jan 22, 2013 at 5:47 PM, Julien Richard-Foy <j...@zenexity.com> wrote:
Oops, my comment was stupid.
Ok, so if we want to not overload apply, the only choice is to define
an implicit conversion from Result to Future[Result]?
On Tue, Jan 22, 2013 at 5:44 PM, Julien Richard-Foy <j...@zenexity.com> wrote:
> Do we need that EssentialAction extends Function1[…]?
>
> On Tue, Jan 22, 2013 at 5:42 PM, Guillaume Bort
> <guillau...@gmail.com> wrote:
>> We can't have
>>
>> apply(f: RequestHeader => Result)
>>
>> and
>>
>> apply(f: RequestHeader => Future[Result])
>>
>> Because both are erased as apply(Function1)
>>
>>
>> On Tue, Jan 22, 2013 at 5:04 PM, Julien Richard-Foy <j...@zenexity.com>
>> wrote:
>>>
>>> > Now because of the current limitations with type erasure
>>>
>>> Which problem are you referring to?
>>
>>
>>
>>
>> --
>> Guillaume Bort, http://guillaume.bort.fr
--
Guillaume Bort, http://guillaume.bort.fr
--
--
Is a seperate execution context wrapped around db calls appropriate? What knobs should people turn, and why?
Should play adopt Havoc P's suggestion from a blog post while back of apps having 2 thread pools one for 'blocking'/io intensive and 1 for CPU intensive? (I'm probably summarizing that incorrectly but hopefully you get the idea)
This is one area that a little codifying of best practices in docs or code could go along way IMO.
If we were to change action so that you had to return Future[Result], then if we get our implicit conversion right (eg, we implicitly convert the action function to be a function that returns Future[Result], not to convert the result to be a Future[Result]), then we can require that a user always selects (by default implicitly brought in by the Controller mix in) an execution context for their action to be executed in.
Transforming a `Result` into an already successful `Future[Result]` is not a blocking operation and doesn't require an ExecutionContext (perhaps it does?).On Wed, Jan 23, 2013 at 3:47 AM, James Roper <james...@typesafe.com> wrote:
If we were to change action so that you had to return Future[Result], then if we get our implicit conversion right (eg, we implicitly convert the action function to be a function that returns Future[Result], not to convert the result to be a Future[Result]), then we can require that a user always selects (by default implicitly brought in by the Controller mix in) an execution context for their action to be executed in.
If you return a Future[Result] in your Action, it means that you have run some logic on an external ExecutionContext, and it's your responsibility to choose which one.
--
* Unfortunately, supporting returning either Result or Future[Result] is not an option, due to type erasure. However, we could define an implicit conversion that implicitly converts Future[Result] to AysncResult, and so fake it. However, this still means we've got this "AsyncResult" that composing actions and filters need to deal with.
“Collisions” caused by type erasure
I have another thought (not completely related, though): do you think
it is a good idea that the default behavior consists in parsing the
request body? Shouldn’t we just ignore it unless we want to use it?
Then we would have the following signatures in the `Action` object:
def apply(block: RequestHeader => Result): EssentialAction
def apply[A](parser: BodyParser[A])(block: Request[A] => Result): Action[A]
Another question: why are forms not directly handled by body parsers?
I imagine something like:
val login = Action(loginForm) { req =>
req.body.fold {
case (user, pwd) => Ok
case errors => BadRequest
}
}
I have another thought (not completely related, though): do you think
it is a good idea that the default behavior consists in parsing the
request body? Shouldn’t we just ignore it unless we want to use it?
Then we would have the following signatures in the `Action` object:
def apply(block: RequestHeader => Result): EssentialAction
def apply[A](parser: BodyParser[A])(block: Request[A] => Result): Action[A]In the case of GET requests, there's no body, so it makes no difference whether there is a parser configured or not. In the case of POST and PUT requests, there is usually a body, and in 99% of cases if there is a body you want to do something with it. So I think having the any content body parser there by default makes a lot of sense. Also note that regardless of whether you want to handle the body or not, you must consume it, since very few clients ever try to read a response until they've finished writing the body. If you don't read the body, you risk getting into a deadlock where the client is getting pushback on the network because you aren't consuming it, and so it isn't trying to read the response yet, and you're trying to send a response but getting pushback from the network because the client isn't consuming it.Another question: why are forms not directly handled by body parsers?
I imagine something like:
val login = Action(loginForm) { req =>
req.body.fold {
case (user, pwd) => Ok
case errors => BadRequest
}
}
I think this makes a lot of sense. There are a few ways we could support this:* Create a form binding body parser, so your code would look something like Action(form(loginForm))
* Create an implicit conversion that convers a form to a body parser, so your code would look just like the above
* Make Form implement BodyParser, so your code would look just like the above
I don't think we should overload Action to accept a Form, because I don't think our core classes should depend on the form API (we might decide to pull it out into its own module, and provide other ways of binding forms, etc).
Request => Future[Response]
--
Well we are not really taking about 2 ways to do the same thing.We all agree that the Action signature should be Request => Future[Result].We are just trying to define the best way to deal with situations where you get a Result directly instead of a Future[Result]. This is a very common situation and create a Future.success(result) seems too artificial.Even if we know that everything is async in play, new users coming to play looking for a simple Java or Scala MVC framework, shouldn't have to deal with asynchronous result and Future at first if they don't need it.So there is 2 subjects in the initial thread started by James:- Changing the Action signature to => Future[Result], so removing the artificial AsyncResult, and simplifying the Actions composition.
AsyncResult is not artificial. It means what it says. If you don't use Async then you ARE blocking. Use Async to not block.
AsyncResult is not artificial. It means what it says. If you don't use Async then you ARE blocking. Use Async to not block.But that's misleading. Consider the following code:import play.api.libs.concurrent.Execution.Implicits._def myAction = Action {Async {Future {return Ok(someBlockingIoCall())}}}Is this blocking or not? It uses Async, according to your statement, it's not blocking. But in actual fact, there is no difference whatsoever, in terms of which thread pool does what, between that code, and this code:def myAction = Action {return Ok(someBlockingIoCall())}And this is what I don't like about async,
it misleads people to think that just because they use it, it means their code is async.
But, whether they use the Async keyword or not has no bearing on whether their action is async or not.
This topic of async by default keeps on coming up all over the place, and so I thought I'd post this email to the dev mailing list so that we can refer people to it in future. Here's the thing, Play actions are async by default. The confusion comes from the fact that we use the word "async" in the code of actions that involve futures, and not in actions that don't, implying that actions that involve futures are asynchronous, and actions that don't are not. So let me explain what I mean by in Play actions being async by default. Here is an action that you might think is not async:def index = Action {Ok(views.html.index("Your new application is ready."))}Truth is, it is async. Note that the "{ Ok(...) }" part of the code is not the method body of index. It is an anonymous function that is being passed to the Action object apply method, which is creating an object of type Action. Remember that! The code of this action that you wrote is not the method body, but an anonymous function. Action is a class with two attributes. One of them is a body parser, and a body parser is just a function that takes a request header and returns an iteratee that consumes byte arrays and produces a body of a particular type, we'll call it A. The second attribute is a function that takes a request with a body of type A, and returns a result. This is where our anonymous function ends up.Now an action is actually just an implementation of the EssentialAction trait. An EssentialAction is a function that takes a request header, and returns an Iteratee that consumes byte arrays and produces a Result. Action implements this by first applying the body parser to the RequestHeader, which gives it an iteratee to produce the body, then mapping this to be a request with a body, and then mapping this using the anonymous function from the action implementation. If you haven't followed me, don't worry. The point of explaining that so to show this, the following code does almost exactly the same thing as the first implementation of the action:def index = new EssentialAction {def apply(rh: RequestHeader) = Iteratee.ignore[Array[Byte]].map { u =>Ok(views.html.index("Your new application is ready"))}}There are a few slight differences, in the code above, we are ignoring the body, where as the first implementation uses the default body parser, which will actually parse the body if one is available to be parsed, even though our code ignores it. Though, in the case of a GET request, there will be no body, so parsing it is irrelevant. The other difference is that the first one will have an intermediate Request[AnyContent] object created, which gets passed to the function passed to Action, and as you can see in our code, is promptly ignored. We're skipping that step here too.So, apart from the minor differences, they are essentially identical, not just in the end result, but in the way they work internally. And, the point I want to make here, is that the second implementation shows very clearly that it is asynchronous. We asynchronously parse the body, then we don't wait for the result, instead we pass our anonymous function to the map method, the same way as you map futures asynchronously. And that's why I wanted you to remember that the core of our action implementation was an anonymous function, it's just a function that gets passed to an asynchronous map method. There's nothing synchronous about it, it's plain old good old asynchronous code. So what's the go with async? Well let's have a look at an async action:def index = Action {Async {WS.url("http://playframework.org").get().map { resp =>if (resp.status == 200) Ok("Play is up!") else Ok("Play is down :(")}}}Implementing this is an EssentialAction could look like this:def index = new EssentialAction {def apply(rh: RequestHeader) = Iteratee.ignore[Array[Byte]].mapM { u =>WS.url("http://playframework.org").get().map { resp =>if (resp.status == 200) Ok("Play is up!") else Ok("Play is down :(")}}}As you can see there's no sign of "async" here, because nothing special is needed, it's already async. The only difference is instead of calling map, I've called mapM. The "M" is a convention used in functional programming (eg haskell uses it) to say this is a monadic operation, it takes a function that returns a container type. If I were to convert the Iteratee to a Future (it is possible to do this), I would actually be calling flatMap instead of mapM, both methods have essentially the same signature.However, this implementation is slightly different from what actually happens, because what actually happens in the first implementation is that we return an AsyncResult, the Async method is just a convenience method for creating an AsyncResult. AsyncResult is not a very good name, it implies that other results are not asynchronous. The existence of AsyncResult I think comes from a combination of how code has evolved from when it was first written (EssentialAction is actually a relatively new thing), and the convenience methods we've used for building EssentialAction (this is the Action class and companion object, it also happens to make action composition really easy). When using the Action convenience methods, you don't have a choice as to whether mapM is called, or map is called, you just pass the function. As it happens, map is called, which is why you can't just return a Future[Result]. Instead, you wrap it in an AsyncResult, and later on Play inspects whether the Result is a PlainResult or an AsyncResult, and if it's an AysncResult, it recurses using the wrapped Futures map function to eventually get to a PlainResult, and handles that.So, let's be clear, things are async by default, but the naming of the helper methods confuses people, making them think there are two types of actions, synchronous and asynchronous. Should we do something about this? I don't know. Here are some options:
* Unfortunately, supporting returning either Result or Future[Result] is not an option, due to type erasure. However, we could define an implicit conversion that implicitly converts Future[Result] to AysncResult, and so fake it. However, this still means we've got this "AsyncResult" that composing actions and filters need to deal with.
* Change the return type of Action to Future[Result]. But then what should actions that don't need futures do? Wrap everything in Future.successful()? That seems like a lot of unnecessary boiler plate to me, especially for unimplemented actions, actions that just redirect, etc. An implicit conversion may be able to be used to convert Result to Future[Result].* Change actions so they receive a Future[Request[A]]. This is option is a fair amount of extra boilerplate, but it does make it completely explicit that all actions are asynchronous, since to handle an action, you will simply map the request. It doesn't however solve the problem for actions that ignore the request.* Allow you to call Action.map { } and Action.mapM { } instead of just Action { }. The first returns Result, the second returns a Future[Result]. Problem here is it's a bit confusing to newcomers, what does mapM mean? Monads? I thought that was just snobby language Haskell developers used to put everyone else off?So anyway, these are my thoughts on async by default.Cheers,James
--
James Roper
Software Engineer
Typesafe - The software stack for applications that scale
Twitter: @jroper
AsyncResult is not artificial. It means what it says. If you don't use Async then you ARE blocking. Use Async to not block.But that's misleading. Consider the following code:import play.api.libs.concurrent.Execution.Implicits._def myAction = Action {Async {Future {return Ok(someBlockingIoCall())}}}Is this blocking or not? It uses Async, according to your statement, it's not blocking. But in actual fact, there is no difference whatsoever, in terms of which thread pool does what, between that code, and this code:def myAction = Action {return Ok(someBlockingIoCall())}And this is what I don't like about async,This is cheating, what if you should not import this execution context? Things become more logical don't they?
As a, relatively new, user I have discovered a LOT about what Async means thanks to this discussion. And also what execution contexts mean for Futures.
I mostly want to get things done, which is one of the selling points of play. When I started using futures I got a message telling me I needed an execution context and I could import the global one. I did just that to get my app working without understanding all the implications. Reading the discussion, I realize I most likely am not doing this right.
I would like to clarify:
- is play using the "global" execution context to process actions ?
Here is a humble suggestion:
- Could play define additional execution contexts with a few knobs to adjust them and expose them through the api: for example
play.api.ExecutionContexts.persistence
play.api.ExecutionContexts.ws
...
Having a couple settings commented in application.conf allowing to configure the most obvious params on these contexts.
This way we can document the examples by using these execution contexts. This will make sure beginners don't make the mistake of using only one execution context...
--
jean
--
You received this message because you are subscribed to the Google Groups "Play framework dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework-...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
You received this message because you are subscribed to the Google Groups "Play framework dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framework-...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Ok, so some definite action items have come out of this:* Document execution contexts* Document a list of different approaches/best practices on how to use thread pools