Sunday, December 28, 2008

More On My Scala Query DSL

I know I promised to explain some things that I still haven't explained, but unfortunately I won't be doing that just yet. I've been too busy coding. I made some cool additions to what I do have though. Now, I can query over more things that just a dictionary of words. In this post I'll show some examples using music scales. Additionally, I added orderBy, and limit stuff, which was really fun.

I've added google code project for this as well, because I kept adding stuff to my cpusimulator project, and it clearly didn't belong. Here's the link: dslplayground.

Here's some examples:


1 val c_maj_and_a_min = contains(Chord("C", Maj), Chord("A", Min))
2
3 var scales_desc =
Scale find(c_maj_and_a_min) limit 4 orderBy (rootChord, Desc)
4 var scales_asc =
Scale find(c_maj_and_a_min) orderBy (rootChord, Asc) limit 3
5
6 scales_desc foreach println
7 println("-------------------------")
8 scales_asc foreach println

Here I'm searching for all Scales (just major and minor scales) that contain Cmaj, and Amin.

On line 3 I'm limiting my search to 4 scales, and ordering the results by the rootChord of the scale, descending.

On line 4 I'm limiting my search to 3 scales, and ordering the results by the rootChord of the scale, ascending. But, notice I've done this in the opposite order as line 3. (The behavior changes though, if I limit the unordered result set before I order it, or after...not sure what to think about that.)

Lines 6,7, and 8 yield:

List(Chord(G,Maj), Chord(A,Min), Chord(B,Min), Chord(C,Maj), Chord(D,Maj), Chord(E,Min), Chord(F#,Dim))
List(Chord(F,Maj), Chord(G,Min), Chord(A,Min), Chord(Bb,Maj), Chord(C,Maj), Chord(D,Min), Chord(E,Dim))
List(Chord(D,Min), Chord(E,Dim), Chord(F,Maj), Chord(G,Min), Chord(A,Min), Chord(Bb,Maj), Chord(C,Maj))
List(Chord(C,Maj), Chord(D,Min), Chord(E,Min), Chord(F,Maj), Chord(G,Maj), Chord(A,Min), Chord(B,Dim))
-------------------------
List(Chord(A,Min), Chord(B,Dim), Chord(C,Maj), Chord(D,Min), Chord(E,Min), Chord(F,Maj), Chord(G,Maj))
List(Chord(C,Maj), Chord(D,Min), Chord(E,Min), Chord(F,Maj), Chord(G,Maj), Chord(A,Min), Chord(B,Dim))
List(Chord(D,Min), Chord(E,Dim), Chord(F,Maj), Chord(G,Min), Chord(A,Min), Chord(Bb,Maj), Chord(C,Maj))


The fantastic thing about all this is the implementation. I've genericized the stuff I did with Scrabble in a really clean way, and to add search support for an already existing Scale library all I had to do was:


object Scale extends Search[Scale]{
def all = notes.map(MajorScale(_)) ::: notes.map(MinorScale(_))
def rootChord( s: Scale ) = s.rootChord
}

object ScaleMatchers {
def contains(chords: Chord*) = (s: Scale) => {
chords.foldLeft(true){ (b,c) => b && (s contains c) }
}
}

By extending the Search trait, I get all sorts of nice ability to search. All I need to do is provide the definition of the all function. Then, I just define some matchers (in this case only one), and I'm all set to start searching through my data.

Hopefully I'll be able to extract out some of the common matcher stuff as well. I don't think this will be all that difficult.

Its important to note a choice that I've made here. I'm forcing the definition of the all method. Basically, right now, every search loads all the data, and then works on it. Certainly this won't scale, but, this is just a simple little DSL. The underlying implementation could be optimized later. Doug Lea taught me that that is the way you should design languages. First, work on getting the language correct, then worry about performance.

No comments:

Post a Comment