I'm coming down with a serious case of the DSLs!
Back in May chromatic published a tongue-in-cheek checklist to determine whether your code is a DSL or an API. Regular readers know that I've done quite a bit of fooling around with various embedded DSLs in Java and I assure you that getting serious with embedded DSLs is contagious. Check out this snippet from a calendar integration framework I've been working on for the last few days.
ToDo planningTask =
Plan.ToDo("Plan project X").
StartingNow.
MustBeCompletedBy("2007.08.17").
ClassifyAs("Public");
planningTask.Save();
Event planningMeeting =
Plan.Event("Project planning meeting").
RelatedTo(planningTask).
WithPriority(1).
At("Head office").
OrganizedBy("jane@megacorp.com", "Jane Doe").
StartingAt("12:00").Lasting(45).Minutes.
Attendants(
"peter@megacorp.com",
"paul@megacorp.com",
"mary@contractor.com").AreRequired.
Attendant("john@megacorp.com").IsOptional.
Resource("Projector").IsRequired.
ClassifyAs("Public").
CategorizeAs("Businees", "Development").
Recurring.Until(2008).EverySingle.Week.On(Day.Thursday).
Except.Each.Year.In(Month.July | Month.August);
planningMeeting.SendInvitations();
So is it a DSL or API? Lets take chromatics test.
1. Have you ever programmed in a language other than Ruby? (PHP and HTML don’t count.) If not, it’s a DSL.
Yes, the code is C# so I've done that.
2. Is the defining syntactic feature that you’ve cleverly left the parentheses off of a list of function arguments? If so, it’s a DSL.
Nope, can't be done in C#.
3. Is the code primarily a list of key-value pairs? Welcome to DSL Town, population you!
No. This is strongly typed.
4. Does the code require the liberal use of eval() or equivalent? DSL, yay!
To some extent. For instance you can write "12:00" to specify a TimeSpan of twelve hours, so this relies on some parsing. Of course you can supply a real TimeSpan if you're iffy.
5. Have you ever used the phrase “… and it reads just like English!” in seriousness? You’d better get to the hospital; you’re coming down with a case of the DSLs!
Check. I wrote this phrase in my JavaZone abstract. I actually tested this snippet on my computer illiterate girlfriend and she could make sense of it.
6. Are there colons on the front of otherwise-normal English nouns? Did it hurt when your DSL fell down from heaven?
7. Is your code full of language-specific idioms, such as the placement of curly braces and the pipe characters? Smells like a DSL in here!
Oh yes! Not colons but all sorts of other punctuation characters. Didn't feel all that bad, but I must admit that things like..
Recur.Times(20).Every(10).Week.On(Day.Tuesday & Day.Thursday);
...looks a little odd. (It should read "Recur 20 times on Tuesday and Thursday every 10th week")
8. Are examples of the code in question pervasive throughout your entire project rather than localized to particular sections in terms of encapsulation? Pardon me, I think you have a little DSL on you.
No. The DSL builds on top of a vanilla API which works like this:
Event @event = new Event (
new Organizer(new UriParameter("mailto:jsmith@host1.com"),
new LanguageParameter(CultureInfo.CurrentCulture),
new CommonNameParamter("JohnSmith"),
new DirectoryParameter("ldap://host.com:6666/o=3DDC%20Associates,c=3DUS??(cn=3DJohn%20Smith)"),
new SentByParameter("MAILTO:jsmith@host1.com"),
new ExtensionParameter("Extension1", "Some value"),
new ExtensionParameter("Extension2", "Some other value")
),
new Timestamp(1997, 9, 1, 13, 0, 0, new ExtensionParameter("Hello", "world...")),
new StartTime(
new DateTime(1997, 9, 3, 16, 30, 0, DateTimeKind.Utc),
new TimeZoneIdentifier(TimeZone.CurrentTimeZone)),
new EndTime(new DateTime(1997, 9, 3, 19, 0, 0, DateTimeKind.Utc),
new TimeZoneIdentifier(TimeZone.CurrentTimeZone),
new ExtensionParameter("Stuff", "More stuff")),
new Summary("Annual \nEmployee\n \\Review",
new LanguageParameter(CultureInfo.CreateSpecificCulture("en-US")),
new AlternativeTextRepresentationParameter(new Uri("http://www.host1.com")),
new ExtensionParameter("Expand", "Your mind")
),
new Classification(Classification.Private, new ExtensionParameter("This", "That")),
new Categories(new string[] {"Business", "HR"}, new LanguageParameter("sv"))
);
9. Did you build your own parser and interpreter? Haha… oh wait, that might really be a DSL.
No, it's plain old C#.
10. Did you merely choose names for identifiers that match the language of the problem domain? Sorry buddy, that’s just an API. Try again next time!
Sort of. The identifiers relate to the problem domain, but there is quite a lot of syntactic sugar to make the language more humane. For an example, consider the Attendant and Attendants methods of the event planning DSL. These do exactly the same thing but they have different signatures to make the language read better.
IAttendantInfo Attendant(string attendant);
IAttendantsInfo Attendants(params string[] attendants);
The only difference between the IAttendantInfo and the IAttendantsInfo interface is that the first has singular naming of its members while the other has plural naming.
So is it a DSL or is it just an API? It is both. And I believe that developing an API and a DSL to uses that API is the best way to go. This keeps things from getting tangled up in each other. So which would you rather use the DSL or the API?
Update: Rather than reading chromatic's post you should listen to it. It makes it even more funny when a really dry computerized voice reads out the ten checkpoints.