Sunday, August 16, 2009

Vending Machine: A Modest Groovy Example

I wasn't ready for the Language Shootout at the Lambda Lounge, but I've completed an example of the vending machine, and placed it up at GitHub. (The Language Shootout used a small specification to illustrate languages ranging from Haskell to Fan, and everything in between.)

Highlights

As Lambda Loungers know, these examples are not intended to be submissions for the Turing Award. My example is a modest program, but offers this to Groovy newbies:

  • The project uses Gant, has test cases, and is fairly complete.
  • The program accepts an input file, and supports a VERIFY action to "assert" the expected state of the machine. In some ways, the file could be an acceptance test (reminiscent of FIT).
  • One interesting idea (IMHO) is the execution of data as code. More below.
  • The example has some internal uses of Expando, and many uses of the elegant closure-based iterations.
Evaluating Data

Consider a command like so:


// N = Name, P = Price, C = Count
SERVICE [5,5,5,5] [[N:'A', P:'65', C:'10'],[N:'B', P:'100', C:'10']]

The pattern here is: Action Coins Inventory. In this example, Coins is evaluated as a Groovy list; Inventory is a Groovy map. This not only simplifies parsing, but affects the architecture of the example. This is hardly new (hello, Lisp!), but a powerful tool.

Java-esque

As with many languages, Groovy supports a wide-range of styles. I've dubbed this example as "Java-esque". Here's why:
  • Actions (like SERVICE) are objects. This is a nod to Java's style.
  • It does not use the MOP (see Matt Taylor's example), and is not really a DSL.
  • It eschews some common Groovyisms (e.g. using an expression as the return value, without specifying the return keyword).
Known Issues

There is no REPL-loop or interaction with the user: only file input. Also, it emits no output per se, and does not acknowledge corner-cases, such as requiring exact change. It simply allows verification of expected state.

Lessons Learned
  • I love evaluation of data as code. I always have.
  • Tests are essential with Groovy, and they become inextricably tied to your experience as a developer. This is hard to explain, but because of the dynamic types, the tests cement themselves into your dev cycle in a way that is much stronger than Java. If you cheat with a large method that does not have a test, you'll probably pay for it.
  • It is important to read the output when a test fails. Often, I just scan it and blithely assume I know where the problem is. This usually leads to frustration, until I realize that the test was trying to help me all along.
  • As an aside, this is my first project in Git. It is excellent and definitely worth studying.
The Gist

I hope this example helps someone. I hope to write some others, including a full-on Groovy MOP version.

No comments: