One pattern that I particularly like is using inline objects as a substitute for flat properties. I picked this up from using Lift's Mapper/Record ORM. For example, if you are used to ORMs like Hibernate or Rails' ActiveRecord, you are used to using simple properties to map database columns. So if you had a user table with a name column, then the standard way to map this to a Java class would be like this:
@Entity class User{ private String name; @Column public String getName() { return name; } public void setName(String name) { this.name = name; } }Clearly you could follow a similar pattern in Scala. However, Lift does the following:
class User{ object name extends MappedString(this, 100) }Remember that in Scala, an object is a singleton (in this case, there will be a single instance of name per instance of User). Thus an object declaration is like a class declaration, hence it can extend another type (or multiple types, via Scala's traits.) So in this case, the subclassing provides type information about the name object. You also get methods on name courtesy of the subclassing.
What is really cool is that you can declare methods on the object. Let's take an example of how this can be useful. A particularly useful class in Android is the ListView. It has a header and a footer, and has methods for adding child views to either the header or the footer. Here is some Scala sugar for this:
class ListView(baseListView:android.widget.ListView){ object header { def +=(v:View)=baseListView.addHeaderView(v) } }So now you can use this like:
myListView.header += someOtherViewVery nice. But wait, there's more. ListView, as the name suggests, has a list of items. These items can be checked, and there are access methods: isItemChecked(position) and setItemChecked(position, isChecked). I wanted a method so that an application developer could do something like:
if (myListView.items(i).checked) { // do something } // and for changing state myListView.items(i).checked = trueSo how to do this? I got some help from Daniel Spiewak:
class ListView(baseListView:android.widget.ListView){ def apply(position:Int) = new { def checked = baseListView.isItemChecked(position) def checked_=(isChecked:Boolean){ baseListView.setItemChecked(position,isChecked) } } }Pretty cool, huh? I had never seen this syntax before. The apply method is "standard" sugar that let's treat the object as function, i.e. the parameters in the apply method look like they are parameters to the object. The = new {} is just like creating an anonymous class in Java. Next, I wanted to use this technique to manage child views in general. You see, ListView extends ViewGroup, which has generic addView methods. This method is overloaded. One version just takes a single view, so that's easy:
class ViewGroup(base:android.view.ViewGroup){ object views { def +=(v:View) = base.addView(v) } }Pretty obvious at this point... However, another version of addView takes both a View and an insert into the ordered list. I thought that this kind of syntax would be intuitive:
myListView.views(i) = someViewHere is how I coaxed that to work:
class ViewGroup(base:android.view.ViewGroup){ object views { def +=(v:View) = base.addView(v) def update(index:Int,v:View) = base.addView(index, v) } }This is another case of standard syntactic sugar in Scala. It allows exactly the syntax we wanted. Much thanks to both Daniel and to Steve Jenson for this tip.
Genius genius genius! Love it.
ReplyDeleteCheers
Stephan - a Scala user and DDD proponent
http://www.codemokeyism.com
Hi, thanks for the neat examples. Newb question for you - later examples of your final example, ViewGroup replace your update method with this:
ReplyDeletedef apply(index:Int) = new {
def update(v:android.view.View) = base.addView(index,v)
}
What on earth is going on there? Wasn't vanilla update perfect for handling
myListView.views(i) = someView
Thanks for the blog, keep it coming!