Where do we need it? I can name at least 2 areas: persistence and GUI. Absence of ScalaBeans (a la JavaBeans) is in my humble opinion the major limiting factor for development of native Scala frameworks in this areas and integrating existing Java frameworks with Scala. As an example: wouldn't it be nice if you could code JSF managed beans using vars (without ugly annotations), Options, Scala Collections? Think about other great Java libraries which rely on JavaBeans specification and can discover your data structures at runtime and serialize/deserialize properly in other forms, suitable for persistence, data transfer or GUI.
At first this absence might look like a non-issue. If you really think it is not, please answer me couple of questions: what is a property in Scala? There are no getters and setters which begin with "get" and "set", how do you recognize them at run-time? Do you want to deal with nulls or require each optional property have Option type? Which implementations do you want to use for collection interfaces by default? I faced all this issues when working with Scala and thought that it would be nice to have common ground with other developers when answering them. There is much more, believe me, and answers are sometimes tricky and not so obvious. For sure when you work with reflection. And then, even if you know the answers and agree with the rest of your team on them - there is no standard framework to support it.
Here I want to start with some of this questions and give my answers to them. Feel free to provide your feedback - it is all welcome.
PropertiesLet's start with the basic question: What is a property in Scala? When we declare a val, following things happen: Scala compiler generates a private field and a method with the same name. For private[this] only private field is generated, no getter. When we declare a var, compiler generates a private field, getter and setter (here and further I mean Scala getters and setters: getter has the same name as property, setter adds '_eq$' to the property name). We can also declare getter and setter functions explicitly in the code, there will be no field member generated (unless we do it ourselves explicitly).
So far so good. Now the tricky part: do we want to discover non-public properties? From the GUI point of view not: we are not interested in intimate state of the object, we want only see how it looks like (only public members then). From the persistence (and data transfer) point of view it is the other way around: we do not care about how it looks like, we do care about it's state and how we can reproduce it later. Most JPA Entities code use fields to discover object properties (it is an option there - you can use either fields or JavaBean getters/setters).
So, we get 2 views (2 discovering strategies) at an object via reflection:
- private field members
- union of: matching public getter+setter (read-write property), matching private field + public getter (read-only property), public setter (write-only property)
Default constructorsOk, we discovered all the properties, but how do we instantiate an object? Java took the easy way: default constructor with no args. In Scala this very limiting approach - you will cut off the case classes in this way, to say at least. Then it will also limit possibilities for immutable objects with all the consequences of this choice. And it is so easy to declare vals and vars directly in the constructor - much more elegant than in Java. I cannot live without all this stuff.
I want reflection framework to provide me the names of the properties used in the constructor. However this rises another issue: if you chose for public properties only for your property discovery strategy then all your constructor parameters must be public vals or vars. This requirement becomes tricky to achieve when you use inheritance. Look at this code:
class This(val p1:String)
class That(myP1:String, val p2:String) extends This(myP1)
Constructor parameter names can be discovered by paranamer (constructor bytecode parsing library, works also with Scala objects), but myP1 is not public, so your view of the object is not sufficient to instantiate it. If we use 'field' strategy, here is another issue: same value will appear twice: once as "p1" and once as "myP1".
If you have ideas how to deal whith this, let me know.
Type informationReflection was so easy without generic types. When we move to generics we have to deal with following type classes: Class (a.k.a. erasure, no generics information), ParametherizedType, WildcardType, TypeVariable, GenericArrayType. It is tricky to decide at runtime if a type is subtype of another type (do not confuse with inheritance - there are no problems, subtype has more generic definition, including covariance and contravariance). I can live with 'subtype' ambiguity, but I just want to be able to make good guess about type parameter value (existential type) in easy way. Manifest is good approximation of what I want, but there is no way to create Manifest from java.lang.reflect.Type object. Reflection framework must provide it with documented 'best effort' strategy to deal with existential type ambiguity at runtime.
//TODO: provide examples and more explanation
Optional fieldsWe do not use nulls in Scala, we have Option. Period. Optional properties must have Option type and use it properly - we all agree about this, don't we? Reflection framework has to deal with null and Some(null) however by converting them to None on the fly. For easy integration with Java it has to provide at least following functions for property descriptors: javaType (actual value type), javaGet, javaSet (nullify None and unbox Some).
CollectionsScala collections neither subclass nor implement Java collections. Those are 2 distinct implementations. There is JavaConverters object in Scala that provides conversions between these libraries using wrappers. I think it is a good idea to have here same functions as for Option fields: javaType (returning Java collection type), javaGet, javaSet (converting on the fly Java to/from Scala collections).
Another useful thing is getting a Builder instance for corresponding collection. It seems to be possible to get companion class at runtime using reflection (yes, I know, ugly and not reliable, too much dependency on how things are actually compiled, but works for Scala 2.8.1). This doesn't work for sorted collections however since they need Ordering object which is usually provided with implicits. Another way to get a Builder is to require all collection type properties to be initialized with an empty collection. This can be read by reflection and getting new builder from it is very easy. This choice has the downside: we cannot get a Builder before instantiating an object.