Tuesday, May 09, 2006

DRY code, DAMP DSLs

Everyone knows code should be DRY, but does the same rule apply to Domain Specific Languages? Based on my experience designing a DSL based system for a major bank, I believe the answer is no.

One reason for using a Domain Specific Language is to separate the business rules from the complexities of designing the system. When done correctly, the business rules can be maintained by business users who are the most familiar with the problem space. To a business user a DSL should be no different than a group of phrases that describe the rules for running the business correctly.

A well designed Domain Specific Language will appear as Descriptive And Meaningful Phrases.

In my previous post I described a DSL designed for running a poker room. I defined the syntax of the DSL as:
if the '$5-$10 Limit' list is more than 12 then notify the floor to open
In the example I defined a bubble method that created methods for each member of the list passed to bubble. Each method that was defined by bubble took one value and returned the same exact value. This allowed the DSL to be verbose enough that not every word was required to have meaning. In fact 'the', 'list', 'is', 'than', and 'to' are all bubble methods. Without bubble methods the DSL can be DRYed out to read:
notify floor open if '$5-$10 Limit' more 12
However, the meaning of the business rule has been lost at the expense of removing the duplication.

As businesses change, their software requires changes also. By providing a meaningful syntax such as "if the '$5-$10 Limit' list is more than 12 then notify the floor to open" the software can be altered by poker room managers, casino executives, or anyone well educated on poker room management. However, with every word that is removed the message becomes less descriptive and thus less maintainable.

A sure sign that there is room for improvement is when a DSL requires training to understand. An ideal DSL contains phrases that are descriptive and meaningful enough that they require no explanation at all.

Saturday, May 06, 2006

Executing an internal DSL in multiple contexts

Creating an internal DSL (or embedded DSL) is challenging, but it does provide many advantages. One advantage to expressing your business rules in an internal DSL is the ability to execute them in various contexts. By executing the DSL in various contexts you can generate multiple behaviors from the same business rule. When the rule changes over time, all parts of the system that reference the rule will also be changed.

For example assume you work for a casino and you have been tasked with designing a system that will notify the poker room employees when a new table needs to be opened or when you are looking to open a new table. The rules for opening a table vary based on the stakes of the table and the length of the waiting list. For example, you need more people waiting for a no limit game because people go broke more quickly and you don't want the table to be short-handed shortly after you open it. The rules would be expressed in your DSL like this:
if the '$5-$10 Limit' list is more than 12 then notify the floor to open
if the '$1-$2 No Limit' list is more than 15 then notify the floor to open
if the '$5-$10 Limit' list is more than 8 then notify the brush to announce
if the '$1-$2 No Limit' list is more than 10 then notify the brush to announce
The first context in which I will execute the DSL is the context that notifies the employees.
class ContextOne < DslContext

bubble :than, :is, :list, :the, :to

def more(value)
'> ' + value.to_s
end

def method_missing(sym, *args)
@stakes = sym
eval "List.size_for(sym) #{args.first}"
end

def floor(value)
__position(value, :floor)
end

def brush(value)
__position(value, :brush)
end

def open
__action(:open)
end

def announce
__action(:announce)
end

def __action(to)
{ :action => to }
end

def __position(value, title)
value[:position] = title
value
end

def notify(value)
[@stakes, value]
end

end
ContextOne uses the DSL to check the List for the size per stakes and sends notifications when necessary. This is of course sample code and my List object is just a stub to verify that everything works correctly. I'll add a link to the sample code at the end of the post.

Based on this same script you could execute a second context that returns a list of the different games that are currently being spread.
class ContextTwo < DslContext

bubble :than, :is, :list, :the, :to, :more, :notify, :floor, :open, :brush

def announce
@stakes
end

alias open announce

def method_missing(sym, *args)
@stakes = sym
end

end
As you can see, adding additional contexts is very easy. Another could be added to display all positions that are set up to receive notices.
class ContextThree < DslContext

bubble :than, :is, :list, :the, :to, :more, :notify, :announce, :open, :open

def announce; end
def open; end

def brush(value)
:brush
end

def floor(value)
:floor
end

def method_missing(sym, *args)
true
end

end
Executing a DSL script in multiple contexts begins to blur the line between code and data. The script 'code' can also be executed to do things such as generate reports (i.e. A report of which employees are contacted by the system). The script could also be executed in a context that will show how long before a table will be opened (i.e. the rule states that 15 are needed, the system knows 10 are on the list so it displays the message '5 more people needed before the game can start').

A note on implementation: In my experience it is much easier to create a class per context and execute in the scope of that object. An alternative is to execute the script in the scope of one object that creates a generic object graph. The problem with this approach is finding an object graph that is generic enough to be useful in several situations. Clearly, I prefer the first approach.

Sample Code: http://www.jayfields.com/src/dslcontext.txt

Friday, May 05, 2006

IntelliJ Ruby Syntax Highlighting Plugin

I'm very happy to see that the previous hack I detailed for IntelliJ is no longer necessary.

Following my post (a few days later) I got this comment:
Quick follow-up: I spent quite a few evenings working on a generic highlighter (with basic Ruby highlighting as the example config). You may want to have a look here.

Cheers, Daniel

My initial impression was not very good. Then I realized that few options in Colors & Fonts (CTRL + ALT + S, F) were set by default. I made a few quick changes to the color scheme that I prefer and was quite pleased.

Also, I made a few changes in the config to add some extra support:

In simplesyntax_ruby.config I added:
regex SYMBOLS => :[a-z][A-Za-z0-9_]+
descriptions[ SYMBOLS ] = Symbols

This change supports highlighting symbols.

I also modified the tags\keywords.rb file change my keyword array to be:
KEYWORDS = ["alias", "and", "BEGIN", "begin", "break", "case", "class", "def", "defined", "do", "else", "elsif", "END", "end", "ensure", "false", "for", "if",
"in", "module", "next", "nil", "not", "or", "redo", "rescue", "retry", "return", "self", "super", "then", "true", "undef", "unless", "until", "when",
"while", "yield"]


Overall, super job Daniel.

Monday, May 01, 2006

Setting up a printer

As a consultant I'm constantly working from different locations and printing to foreign printers. For some reason, printers seem to be a black art to most of us who are otherwise quite comfortable working with technology. I too used to fear printers until Pat Sarnacke showed me the one true way to enlightenment: Printing to a Standard TCP/IP Port.

steps to printer nirvana:
  1. Print the configuration of the printer to get the printer's IP address
  2. Plug in to the same network the printer is on. (ping if necessary to find out)
  3. Open the windows add printer wizard
  4. Select "Local printer attached to this computer" AND uncheck "Automatically detect and install my Plug and Play printer"
  5. Select "Create a new port:"
  6. Set "Type of port:" to "Standard TCP/IP Port"
  7. The Add Standard TCP/IP Printer Port Wizard should appear, click Next
  8. Enter the IP address into the "Printer Name or IP Address" textbox
  9. Use the default "Port Name", click Next (The 2nd wizard should close)
  10. Select your printer from the list
  11. Give your new printer a name
  12. Do not share, print a test page, and click Finish because you are done.
You now possess the knowledge to print from any random printer your heart desires.

Why do *YOU* like Ruby?

An old friend of mine recently sent me this:
In short, why do *YOU* like Ruby?
It's a fair question. I'm currently developing exclusively in Ruby and writing almost only about Ruby. The short answer is Ruby requires less to get the job done. I'll elaborate, but remember, this is why *I* like Ruby, not an attempt to compare Ruby to any other language.

#1. It's easy to add behavior to objects. You can extract patterns and put them in modules or add them to existing classes. Once you have added the behavior you can quickly add responsibilities to your objects with much less code. Since I've been developing in Ruby I've found myself using Attribute Initializer and Initialization Chain quite often. By having the patterns for both of these behaviors extracted out to class methods I can reduce the code necessary which also reduces the potential for errors.

#2. Ruby's duck typing allows me to easily create dependencies. I strongly believe in the Unit Testing Guidelines I previously posted. The guideline I suggest in that entry is: Only one concrete class (with behavior) should be used per test. Because of duck typing I can follow this rule by creating stub dependencies without worrying about interfaces. Consider the following class:
class Consultant
def initialize(boss, phone, laptop)
@boss, @phone, @laptop = boss, phone, laptop
@phone.on if @phone.off?
@laptop.on if @laptop.off?
end

def text_boss(message)
@phone.text(message,@boss.number)
end

def send_email(email)
@laptop.send_email(email)
end
end
Consultant has a dependency on Boss, Phone, and Laptop. Additionally, when a Consultant instance is created it calls the on method of both the phone and laptop. Because the Consultant instance calls methods on phone and laptop in the constructor, nil cannot be passed as either argument.

Now, I want to test the Consultant class in isolation so I need to create stubs for Phone, Laptop, and Boss. In a statically typed language I would need a Phone, Boss and Laptop interface and each stub would need to implement the full behavior of the interface. However, in Ruby I can create simple stubs that only define behavior I'm interested in:
class StubLaptop; def on; end; def send_email(e); end; end
class StubPhone; def on; end; def text(msg, num); end; end
class StubBoss; def number; end; end;
Now I can use these stubs and define mocks that allow me test only the isolated Consultant class and it's interactions. Also, these stubs are really only in the test for support, thus making them essentially noise. The less noise I am required to include in a test the more readable (and maintainable) the test becomes.

#3. The dynamic features of the language allow me to more easily write Domain Specific Languages (DSL). At my current project we've provided the client with a domain specific language that allows definition of variables and reuse of those variables later in the script.

In the script a variable is defined similar to define :jay => thoughtworks.developer. Obviously, we are really using Ruby to call the method define with a hash where :jay is the key and thoughtworks.developer is the value. When the define method is executed it defines a method dynamically by the key and sets the return value to the value of the hash. Later in the script the variables are used similar to work jay, mike, charles. Again, no magic here, the work method is being called with the return values from the jay, mike, and charles methods. However, the end result is a very readable DSL which empowers my business users to code part of the system.

The same functionality could be achieved by changing the work line to read work :jay, :mike, :charles and storing the values in a hash. But, this is a step down from the above syntax in most business users eyes.

Similarly, I recently had a requirement build an ad-hoc sentence using dot notation. For example, this.was.the.syntax.but.the.sentence.needs.to.be.free.form. By using method_missing I was able to support this style with very little code required other than protecting keywords.