Unboxing Loc.currentValue to get the value

51 views
Skip to first unread message

Adrian Black

unread,
Oct 12, 2012, 3:46:22 PM10/12/12
to lif...@googlegroups.com
This is probably very easy and probably just demonstrates my lack of Scala knowledge.

I have the below code snippet. At 1. param is Box{Any]. Lets say it contains a tuple of (String, String), I cant for the life of me figure out how you unbox it to get the tuple. At 2 there is a syntax error as param is Box{Any]  so it only wants the unboxed value to be type Any but then i cant use it at 3 as Any doesnt support ._1 etc.

val param = for {
          loc  <- S.location ?~ "no value"
          param  <- loc.currentValue  ?~ "no value"  
    } yield param
//1
   
  var threadBox : Box[code.model.ForumThread] = for {
          ident : (String,String) <- param ?~ "no value"//2
          p  <- code.model.ForumThread.getByIdent(ident._1,ident._2)  ?~ "user no found"   //3
    } yield p
   

Antonio Salazar Cardozo

unread,
Oct 12, 2012, 4:29:38 PM10/12/12
to lif...@googlegroups.com
So the problem here is fundamentally that Scala is inferring loc.currentValue to be a Box[Any], so param ends up being a Box[Any].

Now, my experience with SiteMap isn't *super* extensive, but I think this is because S.location is not meant to get you the location the way you're using it. Typically, when you have a param that you build using Menu.param[T], you assign the Menu.param result to, say, a variable called menu. Then you can use menu.toLoc.currentValue to extract the actual value of the param. At this point, you've preserved the type safety, because the result of Menu.param[T] is ~ a ParamMenuable[T]. The toLoc method of a ParamMenuable[T] yields a Loc[T], and its currentValue method in turn yields a Box[T]. Thus, you maintain the typing information throughout that chain.

However, if you call S.location, there's no way to know which particular menu item you're referring to, so there's no way to know that we're talking about a Menu.param that gives you a (String,String).

Thus, you have two options here. One is to get the value out of the Menu.param[T] call that you used. The other is to try casting the content of the S.location value yourself. You can do that with Box.asA:

val param = for {
          loc  <- S.location ?~ "no value"
          value  <- loc.currentValue  ?~ "no value"   
          param <- Box.asA[(String,String)](value) ?~ "value was the wrong type"
    } yield param

Box.asA[T](value) checks value. If value is a T, then it returns a Full box with the properly cast value in it. If value is some other type, it returns an Empty Box (which in this case will be converted to a Failure).

The result above should be that param is a Box[(String,String)].

As to the second part, you want to unpack the tuple like so:

  var threadBox : Box[code.model.ForumThread] = for {
          (identServer, identClient) <- param ?~ "no value"//2
          p  <- code.model.ForumThread.getByIdent(identServer, identClient)  ?~ "user no found"   //3
    } yield p

This should yield far more readable code (of course once the identServer and identClient variables are named properly).  But, this will only work properly if the type of param is a Box[(String,String)].

Hope that helps, feel free to ask for any clarification you may need!
Thanks,
Antonio

PS: There may be some type erasure issues with Box.asA[(String,String)], since that's really just Box.asA[Tuple2[String,String]]; I'm not sure about that.

Adrian Black

unread,
Oct 12, 2012, 5:10:20 PM10/12/12
to lif...@googlegroups.com
Thanks Antonio

I have tried the menu.toLoc (below is an example). The problem i have with this version is that it has to be on a singleton object doesnt it? What i wanted was a way to have it (the param value) available in a class instance object that is created per page call,i.e. so an Author snippet instance object is created and i get the author data object from the ORM and then render different snippets on that page e.g. name, books for that specific author.
If i only have a singleton object isnt there a concurrency issue that in between calling name() and books() from the view the value of menu.toLoc can change due to another request? THanks

object Author extends Logger{

val menu = Menu.param[String]("Author", "Author",
                                   s => Full(s),
                                   pi => pi.toString()) / "author"
                                  
 lazy val loc = menu.toLoc
}

Antonio Salazar Cardozo

unread,
Oct 12, 2012, 5:24:06 PM10/12/12
to lif...@googlegroups.com
There's no concurrency issue on menu.toLoc.currentValue. Much like RequestVar and SessionVar, the access is scoped to your current request.

By the way, if you look at menu.toLoc, it's actually already a lazy val. The place where the scoping takes effect is the call to currentValue. So you can absolutely use the code above, and then call Author.loc.currentValue in your snippet and get the correct value.

Sound good?
Thanks,
Antonio

Adrian Black

unread,
Oct 13, 2012, 7:14:30 AM10/13/12
to lif...@googlegroups.com
Ahh ok. So even though its on an object singleton, for simultaneous requests its value will be different per request.

I haven't got far enough with Scala to know what lazy val does just yet. Ill check it out.

Many thanks Anotonio.

Antonio Salazar Cardozo

unread,
Oct 13, 2012, 12:43:02 PM10/13/12
to lif...@googlegroups.com
Yep yep. No problem!

lazy vals are simply vals whose value is still computed once, but not until you first access them.
Thanks,
Antonio
Reply all
Reply to author
Forward
0 new messages