Tuesday, March 2, 2010

Redoing the ArgParser in Scala

So I'm learning Scala and one the first things I've done is re-write the little argument parser sample I did for the Declarative over Imperative piece on 97 Things Every Programmer Should Know. I expected the Scala version of the argument parser to turn out somewhat different, shorter and more readable than the C# version. Whether it did or not I'll get back to towards the end of the post.
Now lets turn to the code. First of all I have a type called Argument which represents an argument that the parser should recognize:

class Argument(val name: String,
val action: String => Unit,
val helpText: String)

This is the complete declaration of the Argument class which is a value type with three immutable properites; name, action and helpText. The parts worth noticing is first of all how Scala combines the type declaration, propery declaration and constructor into a single line, allowing for really DRY declarations of value types like this one. Secondly notice the action property. That's a function type. In this sample the action is the piece of code that should handle the arguments matching the name property.
The declaration of the ArgParser type itself (with the whole implementation yanked out) is:

class ArgParser(private val args: List[String],
argumentSpecification: List[Argument]) {
//code omitted
}
Again the declaration of the class itself and a private immutable property - args - are condensed to a single line. Notice the second argument; a list of Argument objects. That list defines the arguments the parser is able to recognize, but internally I would like to store those arguments in a map from argument names to argument objects instead of as a list. The code that does that transformation is:

private val argumentMap: Map[String, Argument] =
(Map[String, Argument]() /: argumentSpecification){
      (map, a) => map+((a.name, a))
}
Notice the /: method. In Scala a method can be called anything including names consisting of special characters, like /:. The /: method performs a fold left on the list argumentSpecification, i.e. it applies the code block immediately after the call to each element in the list, while passing the result of one such invocation as an argument to the next. The end result is the result of the last invocation. Again I get a pretty concise piece of code.
The actual parsing of the args parameter is done by looping through the args list, looking up matches in the argumentMap, and invoking the action:
def parse {
for(i <- 0 until(args.length, 2)) {
args(i) match {
case "--help" => prettyPrintHelpText
case x if argumentMap.contains(x) => argumentMap(x).action(args(i + 1))
case _ =>
}
}
_hasParsed = true
}
That's pretty similar to the C# version.

The complete argument parser looks like this:
class ArgParser(private val args: List[String],
           argumentSpecification: List[Argument]) {
   args.length match {
     case 1 if (args.first == "--help") =>
     case x if (x % 2) == 1 =>
        throw new IllegalArgumentException
     case _ =>
  }

private val argumentMap: Map[String, Argument] =
(Map[String, Argument]() /: argumentSpecification){
(map, a) => map+((a.name, a))
}
private var _hasParsed = false
private var _inputFileName = ""

def hasParsed = _hasParsed

def parse {
for(i <- 0 until(args.length, 2)) {
args(i) match {
case "--help" => prettyPrintHelpText
case x if argumentMap.contains(x) =>
argumentMap(x).action(args(i + 1))
case _ =>
}
}
_hasParsed = true
}

private def prettyPrintHelpText {
println("Usage:")
argumentMap.values.foreach { argument =>
       println("\t" + argument.name + ": " + argument.helpText)
     }
   }
 }
Apart from a couple of syntactical things, like Scalas way of declaring properties and constructors and its way of accepting closures as arguments to methods, the Scala ArgParser code is pretty close to the C# ArgParser code. Why? Well I see a couple of possible reasons:
  1. I'm only just learning Scala now, so maybe I'm not thinking in Scala idioms and style
  2. Using a map from names to arguments containing lambda function for the handling of the arguments is actually the way to implement this sample, regardless of the language.
  3. C# has sufficiently many functional features that moving to a more functional language doesn't make much of a difference for simple cases like this ArgParser.
Which one is the right explanation I don't know. If you have opion please leave a comment.

No comments:

Post a Comment