2

In groovy one can do:

class Foo {
  Integer a,b
}
Map map = [a:1,b:2]
def foo = new Foo(map) // map expanded, object created

I understand that Scala is not in any sense of the word, Groovy, but am wondering if map expansion in this context is supported

Simplistically, I tried and failed with:

case class Foo(a:Int, b:Int)
val map = Map("a"-> 1, "b"-> 2)
Foo(map: _*) // no dice, always applied to first property

A related thread that shows possible solutions to the problem.

Now, from what I've been able to dig up, as of Scala 2.9.1 at least, reflection in regard to case classes is basically a no-op. The net effect then appears to be that one is forced into some form of manual object creation, which, given the power of Scala, is somewhat ironic.

I should mention that the use case involves the servlet request parameters map. Specifically, using Lift, Play, Spray, Scalatra, etc., I would like to take the sanitized params map (filtered via routing layer) and bind it to a target case class instance without needing to manually create the object, nor specify its types. This would require "reliable" reflection and implicits like "str2Date" to handle type conversion errors.

Perhaps in 2.10 with the new reflection library, implementing the above will be cake. Only 2 months into Scala, so just scratching the surface; I do not see any straightforward way to pull this off right now (for seasoned Scala developers, maybe doable)

Community
  • 1
  • 1
virtualeyes
  • 11,147
  • 6
  • 56
  • 91
  • 2
    It's certainly not straightforward. I don't understand, however, what this question asks that is not addressed by the other one you linked to--could you clarify the difference between your question and that one? – Rex Kerr Jan 05 '12 at 18:29
  • I'm asking specifically about map expansion. In Groovy the map is applied to each class property automatically. I assumed with Scala _* notation that one could apply map elements to a case class in the same manner. Obviously not the case. I also wanted to bring up reflection, since the other thread does not mention this issue. AFAIK, without reflection there can be no run time casting to/from one type to another (e.g. from SQL result set to Scala object to JSON). I'll sacrifice performance, just save me repeatedly typing already defined class properties -- once is enough... – virtualeyes Jan 05 '12 at 19:01

2 Answers2

5

Well, the good news is that Scala's Product interface, implemented by all case classes, actually doesn't make this very hard to do. I'm the author of a Scala serialization library called Salat that supplies some utilities for using pickled Scala signatures to get typed field information

https://github.com/novus/salat - check out some of the utilities in the salat-util package.

Actually, I think this is something that Salat should do - what a good idea.

Re: D.C. Sobral's point about the impossibility of verifying params at compile time - point taken, but in practice this should work at runtime just like deserializing anything else with no guarantees about structure, like JSON or a Mongo DBObject. Also, Salat has utilities to leverage default args where supplied.

prasinous
  • 798
  • 3
  • 6
  • Well, don't stop there ;-) In addition to map-expanded case class instantiation, implement a toJson method that handles objects of arbitrary complexity (you may have already done so, am about to graze on some salat). For scala web development the primary pain points seem to be coercion from SQL result set to case class object and case class object to/from JSON. In dynamic languages these are 1-liners; in scala they tend to be tail-wagging-dog affairs. I prefer run time flexibility over compile time certainty (eager compile time is too early, but lazy run time is JIT ;-)) – virtualeyes Jan 05 '12 at 21:59
  • I have some json code using lift-json in my local git repo. It's not published yet - in the meantime, you can actually just do a toString on DBObject and... usable JSON. Not the fastest or cleanest way, but it sure is convenient. – prasinous Jan 05 '12 at 23:00
  • I actually checked this out the other day. Is Salat NoSQL only or applicable to RBDMSs as well? A Spray user mentioned how happy they were to be using MongoDB with Salat (the implication being that they did not have to manually coerce SQL result sets to scala case class instances). Anyway, let me know if Salat has legs for SQL. Thanks... – virtualeyes Jan 05 '12 at 23:36
  • 1
    Hi, the latest snapshot of Salat code has case class <-> map functionality. I am still considering what to do with SQL but it is definitely on my horizon. – prasinous Apr 29 '12 at 04:31
  • thanks I'll take a look. 2.10 is on the way as well so everyone, library authors and consumers, should get a nice assist with reflection ;-) Since I made this post I've become a bit more flexible re: the requirement for runtime coercion; i.e. the type safe, manual mapping of map to case class instance in many cases is just fine. It's when you have 50+ form fields that runtime coercion becomes more interesting (Salat snapshot may indeed do the trick, will give it a spin, thanks) – virtualeyes Apr 29 '12 at 08:44
  • @prasinous If you could give a link to the relevant code that would be really helpful. – Robin Green May 11 '12 at 13:33
3

This is not possible, because it is impossible to verify at compile time that all parameters were passed in that map.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
  • 1
    It follows that this is impossible for any language based on static compilation; that without knowing the types of one object (the TBD run time paramater map) those of the known object (case class to construct) cannot be matched. A dividing line this is between dynamic and static languages. Thankfully as @prasinous suggests, a run time solution is possible, perhaps even with map expansion (he seems to be suggesting so). – virtualeyes Jan 05 '12 at 21:29