Thursday, June 11, 2009

Case Classes and Product

It's a well-known fact that by adding "case" before "class" in Scala, the compiler will generate some scaffolding for you like making all your paremeters "val", creating apply and unapply in a companion object. etc.

It's not a particular known fact that one of the things it does is extending the class with the trait Product. This trait makes it easy to access the elements of the class, like in the example below:



scala> trait recordType {
| self : Product =>
| def records : String = (
| for(parm <- self.productIterator)
| yield parm.asInstanceOf[AnyRef].getClass.getSimpleName+": "+parm
| ) mkString "\n"
| }
defined trait recordType

scala> case class X(i : Int, c : Char, s : String) extends recordType
defined class X

scala> val x = X(5, 'd', "this")
x: X = X(5,d,this)

scala> println(x.records)
Integer: 5
Character: d
String: this

Let me talk about the trait recordType a bit first. Its body first line declares that "self : Product =>". This has two purposes. First, it creates an alias for "this". Second, and most important, it tells the compiler that to be able to use this trait, a class must mix in the trait Product.

Anyway, the code works fine for simple classes, but it might be weird for collections:



scala> case class Y(a : Array[String], l : List[Int]) extends recordType
defined class Y

scala> val y = Y(Array("this", "example"), List(1,2,3))
y: Y = Y([Ljava.lang.String;@60a517,List(1, 2, 3))

scala> println(y.records)
String[]: [Ljava.lang.String;@60a517
$colon$colon: List(1, 2, 3)

Here, Array is mucked up because Java's Array are mucked up. As for the List, any non-empty List actually belongs to class "::", a subclass of List.

Sunday, June 7, 2009

Function.tupled, Function.curried

Scala's library hides small gems of which little is said. Let me speak of a personal favorite here, the object Function.

This object has a number of methods to construct a function from another function (or others, in the case of "chain").  They are variations of "curried", "tupled", "uncurried" and "untupled". As their names makes clear, they transform a function whose arguments are not curried or are not a tuple into a function whose arguments are curried, or which receives a tuple as an argument. Take, for instance, this definition of zipMap, which applies a function of two arguments between two lists:



scala> def zipMap[A,B,C](l1 : Seq[A], l2 : Seq[B])(f : (A,B) => C) =
| l1 zip l2 map (x => f(x._1, x._2))
zipMap: [A,B,C](l1: Seq[A],l2: Seq[B])(f: (A, B) => C)Sequence[C]

scala> zipMap (List(1, 3, 9), List(5, 2, 3)) ((x,y) => x max y)
res5: Sequence[Int] = List(5, 3, 9)


This is not a bad definition, but accessing the tuple arguments to pass to f is too much mechanics for my taste. So, instead, we could do this:



scala> def zipMap[A,B,C](l1 : Seq[A], l2 : Seq[B])(f : (A,B) => C) =
| l1 zip l2 map Function.tupled(f)
zipMap: [A,B,C](l1: Seq[A],l2: Seq[B])(f: (A, B) => C)Sequence[C]

scala> zipMap (List(1, 3, 9), List(5, 2, 3)) ((x,y) => x max y)
res4: Sequence[Int] = List(5, 3, 9)


Much better, don't you think? By the way, this was done on 2.8, where Seq has the method zip.

Friday, June 5, 2009

Scalable Algorithms 3

Back to matching shortcuts to menu options, I decided my best option would be a deterministic finite automaton. It would make excellent use of partial input, and take much less space than a set of all possible inputs accepted -- to make use of state machine lingo -- by each menu option.

Now, anyone who has ever worked with DFA before will tell you that the trick to build one is to first generate the regular expression which represents what you want to match. Yes, that's your good, old RegExp. Well, not fully... you can't have back references, for one thing. But most of what you think of as regexp will do.

So, what regular expression would match our input? Let's take a small menu option: "New Tab". We want to accept either word as well as its abbreviation, so "New", "Tab" and "NT" should be matched. Any prefix of either word should be taken as well, so we add "N", "Ne", "T" and "Ta" to it. Finally, we want to combine word prefixes, like "NTab" or "NeTa", you get the idea.

So, let's get the first word, and see how far we can take. We have "N", "Ne" and "New". So "N" is mandatory, and "e" is optional. That would be "Ne?" in regexp. Next, "w" is also optional, but it can only appear if "e" appears as well. That's "N(ew?)?" in regexp, or, to make regular use of parenthesis, "N(e(w)?)?". Now, since we may as well only match the second word, the whole first word is optional, which gives us "(N(e(w)?)?)?".

Naturally, each word would follow the same pattern, but what about the whole menu option? As it happens, you only have to concatenate the patterns: "(N(e(w)?)?)?(T(a(b)?)?)?". It should be pretty simply to create such a function.

In fact, we can do it using foldRight. As you know, foldRight combines things from right to left, using the result of the last operation and the next element. Now, it may not be immediately obvious what the operation is, so let's assume we already processed "w". That means we have "(w)?" as the last result, and "e" as the next letter, and the result we want to come up with is "(e(w)?)?". It's trivial to see, then, that the operation is "("+"e"+"(w)?"+")?".

Now that we know what the operation is, let's think about the initial "result" that fold takes as parameter. We want x in "("+"w"+x+")?". Since that must be equal to "(w)?", x = "".

Ok, so now we know what the regular expression for a menu option must look like, as well as how to build one from its string. We can now proceed to building a DFA.

But... we can match regular expressions to strings just as well as DFA. I might not take advantage of partial inputs, and I can't go the next step, which is creating one DFA which will accept all menu options and, as an added bonus, have the associated menu options at each state.

Still, how hard would it be to match against regexp? Well, let's try...



val wordsep = "\\W+"
def words(s : String) = s.split(wordsep)
def phraseRegex(s : String) =
"^" + words(s).map(_.foldRight("")("(" + _ + _ + ")?")).mkString
def regexList(l : List[String]) = l.map(s => phraseRegex(s) -> s)
def regexSearcher(l : List[String]) = {
val r = regexList(l)
(s : String) => r.filter(s matches _._1).map(_._2)
}


That's it. You could, and should improve it by pre-compiling the regexp expressions. It's a bit uglier, but for the sake of doing things right, I'll post it at the end. Anyway, this is fairly inexpensive in terms of memory usage, and regexp matches are very fast. I doubt it can beat a hash tree, but it should give it a run for its money. Still, the fact that you'll be matching every menu option against the input is problematic, and the hash tree doesn't suffer that problem. The setup time for this algorithm should be pretty fast too, even precompiling the regexp.

Anyway, it's short, it's obvious, and it's not too shabby. We'll next get into DFA, and do it by using Scala's standard library. That should be the worst of all algorithms, but a stepping stone to the last one I'll present. For now, here's the revised code.



val wordsep = "\\W+"
def words(s : String) = s.split(wordsep)
def phraseRegex(s : String) =
("^" + words(s).map(_.foldRight("")("(" + _ + _ + ")?")).mkString).r
def regexList(l : List[String]) = l.map(s => phraseRegex(s) -> s)
def regexSearcher(l : List[String]) = {
val r = regexList(l)
(s : String) => r.filter(_._1.findPrefixOf(s) == Some(s)).map(_._2)
}

Wednesday, June 3, 2009

Compiling Remotely

As you may know, the fsc compiler, the recommended way to compile Scala, and used by the Scala interpreter by default, detaches a daemon service the first time it is run. After that, fsc will always call the daemon service to compile for it, to save time.

It does this through TCP, which begs the question... can I compile remotely? The answer is Yes! It's not documented, and it is a bit awkward, but...

First, you get fsc running on the server by running fsc -verbose, like this:



$fsc -verbose
[Server arguments: -d C:\Users\Daniel\Documents\Programas\. -verbose]
[VM arguments: ]
[Temp directory: C:\PROGRA~1\Scala\bin\..\var\scala-devel]
[Executed command: C:\PROGRA~1\Scala\bin\..\bin\scala.bat scala.tools.nsc.CompileServer]
[Port number: 1385]
[Connected to compilation daemon at port 1385]
Usage: fsc
where possible standard options include:
-g: Specify level of generated debugging info (none,source,line,vars,notailcalls)
-nowarn Generate no warnings
-verbose Output messages about what the compiler is doing
-deprecation Output source locations where deprecated APIs are used
-unchecked Enable detailed unchecked warnings
-classpath Specify where to find user class files
-sourcepath Specify where to find input source files
-bootclasspath Override location of bootstrap class files
-extdirs Override location of installed extensions
-d Specify where to place generated class files
-encoding Specify character encoding used by source files
-target: Specify for which target object files should be built (jvm-1.5,jvm-1.4,msil)
-print Print program with all Scala-specific features removed
-optimise Generates faster bytecode by applying optimisations to the program
-explaintypes Explain type errors in more detail
-uniqid Print identifiers with unique names for debugging
-version Print product version and exit
-help Print a synopsis of standard options
-X Print a synopsis of advanced options
@<file> A text file containing compiler arguments (options and source files)

This will get you the port number. Next, you can compile whatever you like from a remote client like this:

$fsc -server 192.168.1.35:1385 strategy.scala

That's it. Cool, eh?