Sunday, September 13, 2009

Some Tasty Scala Sugar

I have finally been getting around to working some more on Scala+Android. A lot needs to be done, especially since I am talking about it next month at the Silicon Valley Code Camp. Scala advocates (and I guess that includes me) often say that is very easy for application developers to use Scala, i.e. that "most" developers are never exposed to the more complex aspects of the language. The folks who "suffer" are APIs developers. I've mostly fallen into the first camp (app developers), but now that I'm trying to create a Scala API on top of the Android SDK, I get exposed to more aspects of Scala. Here are a couple of things that I found interesting.

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 += someOtherView 
Very 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 = true
So 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) = someView
Here 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.

2 comments:

Stephan.Schmidt said...

Genius genius genius! Love it.

Cheers
Stephan - a Scala user and DDD proponent
http://www.codemokeyism.com

Adam Rabung said...

Hi, thanks for the neat examples. Newb question for you - later examples of your final example, ViewGroup replace your update method with this:
def 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!