Reputation: 13372
There is Security Example where you can find => implicit request
for def withUser(...
method.
Based on that example I've created one-file-sample here: https://github.com/Sergey80/scala-samples/blob/master/src/main/scala/implicits/usecases/RequestResponseTest.scala (where is no implicit .. and I do not see why I need use it)
The question is:
why do I need type implicit request
(as was shown in Security Example) if it works without implicit. Since we are already referring on request
as val
(defined above in the code).
Copy-pasting my simplified-sample here (where not implicit is used), this code has the same functions as in initial Security Example (withUser
, withAtuh
) with same set of parameters, the point was just to make it launchable from one file:
package implicits.usecases
object RequestResponseTest extends App {
type User = String // for simplicity sake - all are strings
type Request = String
type Result = String
trait Action // there are two auth Action - Ok and Not Ok
case class OkAction(content:Result) extends Action
case class UnauthorizedAction(content:Result) extends Action
var userDB = List("user1", "user2", "user3") // our simple database
val user = "user1" // usually user available from the session
var request : Request = "request" // current request
// check authorization and wraps request into Action
def withAuth(f: User => (Request => Result) ): Action = {
println("withAuth in play...")
val result:Result = f("user1")(request) // (is user was found or not)
def isAuthorized(user:User): Boolean = {
println("check authorisation...")
true // authorize everyone, welcome :)
}
def onUnAuthorized(request: Request): Action = {
println("wrapped to Action as not authorized")
UnauthorizedAction(request)
}
if (result == "ok") {
if (isAuthorized(request)) {
println("wrapped to Action as authorized")
OkAction(request)
} else onUnAuthorized(request)
} else onUnAuthorized(request)
}
//
def withUser(f: User => (Request => Result)) : Action = {
println("withUser in play...")
def findInDb(user: User): Option[User] = {
userDB.find(u => u == user)
}
val authResult:Action = withAuth ( // call 'withAuth'
/* Create anonymous function to please the 'withAuth'
it is already known who is current user, and what request is, but doesn't what Result is.
And he Result is the fact whether the user exists in users database or not.
So it tries to get the Result by searching for the current user.
If user has been found then it makes sense to continue with Authorisation (withAuth)
*/
user => request => { // passing anonymous function with user, request, and result (where result is based on user existence in the user db )
val userOption = findInDb("user1") // find the user in users db
val result:Result = userOption match { // check if user exists
case Some(_) => // user has been found
println("user has been found")
"ok"
case None => // user has not been found
println("user has not been found")
"not ok"
}
result // "ok" / "not ok" (user has been found or not)
} // end of passing anonymous function to 'withAuth'
)
authResult match {
case OkAction(_) => f(user)(request) // if authorized do The work
}
authResult
} // edn of 'withUser'
// Let's run this and to some work (no `implicit request` here)
def doWork() = withUser { // doWork -> withUser -> withAuth (like Decorator/Wrapper pattern)
user => request => {
// if user exists (withUser) and authorization is ok (withAuth), then this work will done
println("do some important work here!!")
"work is done!" // Result is just a String
}
}
val result = doWork() // doWork doesn't care about user or request
}
/* Output:
withUser in play...
withAuth in play...
user has been found
check authorisation...
wrapped to Action as authorized
do some important work here!!
*/
Update: I understand if implicit was put on type Request to let compiler look up for value of this type which is defined implicitly somewhere. Like Inversion of Control injection. But there in Security Example it is put on value itself. On "request" value. If I'm putting 'request' it automatically means that I already have access to it and compiler knows about it (without implicit).
Upvotes: 1
Views: 102
Reputation: 13372
This simple snippet explained me the thing (let me remember). Kind of essential:
implicit val integer = 3
def function4(implicit i:Int) = i // this works as default variable (ALMOST)
// same as this
def function4_1(i:Int = implicitly[Int]) = i // implicit scope has Int = 3
val result4 = function4 // should be without ()
val result4_1 = function4_1() // requires ()
println("result4: " + result4) // 3
println("result4_1: " + result4_1) // 3
Also I've prepared couple examples here: 1 2 (2 - improving my initial code with Request-Response
case)
Upvotes: 0
Reputation: 55569
One word answer: convenience.
Let's take this simple example from your first link:
def login = Action { implicit request =>
Ok(views.html.login(loginForm))
}
You probably don't need the implicit
here, but it's often convenient to have. Here's why:
This is really calling Action.apply
. In particular, an overload that accepts a parameter:
block: Request[A] => Result
The block
being passed here is the anonymous function:
request => Ok(views.html.login(loginForm))
The Request
is probably not being used at all here, so whether it's implicit or not doesn't really matter. However, Play has many functions which require an implicit Request
(for i18n, checking the uri accessed, whether or not a secure path was used, etc).
If views.html.login
(a Play view) requires an implicit Request
parameter, then it would be very useful to mark request
as implicit, which we have the option of doing so.
implicit request => Ok(views.html.login(loginForm))
All the above code is doing is creating an anonymous function Request[A] => Result
, in which the Request[A]
parameter is made implicit. It isn't looking for an implicit, it's creating one. Similar to:
def anon(req: Request[A]): Result = {
implicit val request = req
Ok(views.html.login(loginForm)
}
Why? It makes for a much less boiler-plate, especially when many methods within the block require Request
parameters. There's also no ambiguity, because the implicit is contained within the block of the anonymous function, and in this example, there is only one Request
that each method should receive (there's only method call in this example, but there could be several more).
Otherwise you'd have to write:
def login = Action { request =>
Ok(views.html.login(loginForm)(request))
}
The withUser
example isn't really any different. It just involves another nested anonymous function where request
is made implicit.
Upvotes: 1