For lift development, I sometimes need to use match
–case
statements like the following. (Rewritten to plain scala for easier understanding.) One note to them: These are actually different partial functions, defined in different parts of the code, so it’s important that a case statement fails in or before the guard in order to have the other partial functions evaluated (if matching fails, that is).
// The incoming request
case class Req(path: List[String], requestType: Int)
// Does some heavy database action (not shown here)
def findInDb(req: Req):Option[Int] =
if(req.path.length > 3) Some(2) else None
Req("a"::"b"::Nil, 3) match {
case r@Req(`path` :: _ :: Nil, 3) if findInDb(r).isDefined =>
doSomethingWith(findInDb(r))
case r@Req(`path` :: _ :: Nil, _) => doDefault
case _ => doNothing
}
Now, in order to know that the case
statement succeeds, I must query the database with findInDb
and check whether the result is valid. Afterwards, I must call it again to use the value.
Doing something like
case r@Req(path, 3) if {val res = findInDb(r); res.isDefined} =>
does not work because the scope of res
is then limited to inside the braces.
I can of course define a var res = _
outside and assign to it, but I don’t feel well doing this.
Is it by any means possible to declare a variable inside the guard? If it is possible to do case r@Req(…)
why not case r@Req() if res@(r.isDefined)
?
You're actually very close. The key piece missing is to use an extractor rather than a guard expression.
object FindInDb{
def unapply(r:Req):Option[Int]= findInDb(r)
}
Req("a"::"b"::Nil, 3) match {
case dbResult@FindInDb(Req(`path` :: _ :: Nil, 3))=> doSomethingWith(dbResult)
case Req(`path` :: _ :: Nil, _) => doDefault
case _ => doNothing
}
Extractors aren't actually required to return only information already present in their arguments, that's just the common use case. You can actually use any partial function, lift it to Option, and be able to match on information about both whether the function is defined and its value.
try this:
object FindInDB {
def unapply(req:Req) => {
//Your sophisticated logic
if(req.path.length > 3) Some((req, 2)) else None
}
then in your case statement you can do:
Req("a"::"b"::Nil, 3) match {
case FindInDb(r, n) => //Now you can see both the Req, and the Int
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With