Statically Typed

because Hindley-Milner rocks

Getting My Head Around Scala Traits


I finally got a chance to work on some code this weekend outside of work and had one of my “Ah-ha” moments when working with Scala Traits.  If you’re a Java refugee then I suggest reading this online posting about the what and why of Scala Traits.  They really do solve many of the problems of multiple inheritance and violations of D-R-Y associated with an interface only model.  And following that, if you’d like to still learn more I suggest this post which compares Scala Traits with Implicit Conversions to Haskell type-classes.

I don’t know much Haskell’s type-class system other that what I’ve read in the above blogs.   However, the people of Boston Area Scala Enthusiasts boast of the Scalaz library, a Haskell derivative inspired work, which I’ve been meaning to get to know.  It’s on my //TODO: list (so is learning to dance, cook, and fly a plane.)

That all being said about Haskell, type-classes, Scala Traits and Implicit conversions without understanding the true power they possessed I wasn’t impressed.  Then I read something that totally blew my mind (see this SO post by Apocalisp.)  Now I’m beginning to understand.

Let’s say that I have a simple container object that has the following within its interface:

class SimpleContainer[A]{
    def map[B]( f:((A) => B) ):SimpleContainer[B]
    def filter( f:((A) => Boolean) ):SimpleContainer[A]
    def reduce[B]( z:B )( f:((A) => B) ):B
    def append( x:A ):SimpleContainer[A]
    def get( x:Int ):Option[A]
    def length():Int
}

These are fairly common functions which you’d find with nearly any implementation of a list, array or set.  And if it’s not a component of the object type itself then they’re provided for in standard libraries (collection classes get all the love.)  And anything missing there could be fixed by writing a set of stand-alone functions taking a pair of iterators.

Now play Devil’s advocate.  What if I wasn’t dealing with something like a traversal of a collection?  Then I’d have to resort to writing a set of functions for a defined interface and wrapping each individual class with a proxy object (D-R-Y compliant.)  You see the problem with that right away, don’t you?  For N classes I’d need N proxies.  And to make that functionality portable within my code I’d need to pass it around via the proxy object, not the underlying object it represents.  Boilerplate would ensue.

With traits I don’t need that boilerplate.  I can add any amount of functionality anywhere I want and call it as if it were part of my original object!  Watch me add merge, mergeIf, rightMergeIf, and leftMergeIf to my SimpleCollection:

trait Mergeable[A]{
    def filter( f:((A) => Boolean) ):Mergeable[A]
    def cycle( f:((A) => Mergeable[A]) ):A
    def append( x:A ):Mergeable[A]
    def merge( input:Mergeable[A] ):Mergeable[A] = {
        input extract( append ) #this is the problem
    }
    def mergeIf( f:((A) => Boolean )( input:Mergeable[A] ):Mergeable[A] = {
        filter( f ) merge( input filter( f ) )
    }
    def leftMergeIf( f:((A) => Boolean )( input:Mergeable[A] ) = {
        merge( input filter( f ) )
    }
    def rightMergeIf( f:((A) => Boolean)( input:Mergeable[A] ) = {
        input leftMergeIf( f )( this )
    }
}

object Mergeable{
    implicit def simpleContainer2Mergeable( input:SimpleContainer[A] ) = new Mergeable[A]{
        def filter = input filter
        def cycle = input reduce( new SimpleContainer[A]() )
        def append = input append
    }
}

That’s it.  Done.  Now I can call any of the functions in Mergeable on any instance of a SimpleContainer class without altering the ability to later on use my SimpleContainer class as a SimpleContainer class. It’s pretty incredible when you think about it for a second.  It allows me to add functionality to any class which can express itself in the form of an append, filter and reduce (foldLeft) simply by stating a new implicit conversion.  It’s the epitome of D-R-Y.

Now, I haven’t actually run this code through a debugger or even in Eclipse (yes, back to Eclipse ’cause of the plug-in support) so I don’t know if it’s syntactically correct.  Also, I couldn’t think of a good name for what to call my modified reduce function.  Cycle sounds too much like foreach.  Criticisms welcome.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Information

This entry was posted on August 17, 2010 by in Scala.
%d bloggers like this: