Internal DSLs in PowerShell

(Harry is on a secret mission in uncharted space this week, so instead of the daily Morning Coffee post, you get a series of autoposted essays. This post combines both some leftover learnings about Ruby from Harry’s Web 2.0 days with his recent obsession with PowerShell.)

My first introduction to the idea of internal DSLs was an article on Ruby Rake by Martin Fowler. Rake is Ruby’s make/build utility. Like most build tools like Ant and MSBuild, Rake is a dependency management system. Unlike Ant and MSBuild, Rake doesn’t use an XML based language. It uses Ruby itself, which has huge benefits when you start doing custom tasks. In Ant or MSBuild, building a custom task requires you to use a external environment (batch file, script file or custom compiled task object). In Rake, since it’s just a Ruby file, you can start writing imperative Ruby code in place.

Here’s the simple Rake sample from Fowler’s article:

task :codeGen do  
  # do the code generation
end

task :compile => :codeGen do  
  # do the compilation
end

task :dataLoad => :codeGen do  
  # load the test data
end

task :test => [:compile, :dataLoad] do  
  # run the tests
end

The task keyword takes three parameters: the task name, an array containing the task dependencies and a script block containing the code to execute to complete the task. Ruby’s flexible syntax allows you to specify task without any dependencies (:codegen), with a single dependency (:compile => :codegen), and with multiple dependencies (:test => [:compile,:dataLoad])

So what would this look like if you used Powershell instead of Ruby? How about this:

task codeGen {  
  # do the code generation
}
task compile codeGen {
  # do the compilation
}

task dataLoad codeGen {  
  # load the test data
}

task test compile,dataLoad {
  # run the tests
}

Not much different. PS uses brackets for script blocks while Ruby uses do / end, but that’s just syntax. Since it lacks Ruby’s concept of symbols (strings that start with a colon), PS has to use strings instead. Otherwise, it’s almost identical. They even both use the # symbol to represent a line comment.

There is one significant difference. For tasks with dependencies, Rake uses a hash table to package the task name and its dependencies. The => syntax in Ruby creates a hash table. Since the hash table has only a single value, you can leave of the surrounding parenthesis. The key of this single item hash table is the task name while the value is an array of task names this task depends on. Again, Ruby’s syntax is flexible, so if you have only a single dependency, you don’t need to surround it in square brackets.

In Powershell, the hash table syntax isn’t quite so flexible, you have to surround it with @( ). So using Rake’s syntax directly would result in something that looked like “task @(test = compile,dataLoad) {…}” which is fairly ugly. You don’t need to specify the square brackets on the array, but you having to add the @( is a non-starter, especially since you wouldn’t have them on a task with no dependencies.

So instead, I thought a better approach would be to use PS’s variable parameter support. Since all tasks have a name, the task function is defined simply as “function task ([string] $name)”. This basically says there’s a function called task with at least one parameter called $name. (All variables in PS start with a dollar sign.) Any parameters that are passed into the function that aren’t specified in the function signature are passed into the function in the $args variable.

This approach does mean having to write logic in the function to validate the $args parameters. Originally, I specified all the parameters, so that it looked like this: “function global:task([string] $name, [string[]] $depends, [scriptblock] $taskDef)”. That didn’t work for tasks with no dependencies, since it tried to pass the script block in as the $depends parameter.

Here’s a sample task function that implements the task function shown above. It validates the $args input and builds a custom object that represents the task. (Note, the various PS* objects are in the System.Management.Automation namespace. I omitted the namespaces to make the code readable.)

function task([string] $name) {
  if (($args.length -gt 2) -or ([string]::isnullorempty($name))) {
    throw "task syntax: task name [<dependencies>] [<scriptblock>]"
  }
  if ($args[0] -is [scriptblock]) {
    $taskDef = $args[0]
  }
  elseif ($args[1] -is [scriptblock]) {
    $depends = [object[]]$args[0]
    $taskDef = $args[1]
  }
  else {
    $depends = [object[]]$args[0]
    #if a script block isn't passed in, use an empty one
    $taskDef = {}
  }

  $task = new-object PSObject
  $nameProp = new-object PSNoteProperty Name,$name
  $task.psobject.members.add($nameProp)
  $dependsProp = new-object PSNoteProperty Dependencies,$depends
  $task.psobject.members.add($dependsProp)
  $taskMethod = new-object PSScriptMethod ExecuteTask,$taskDef
  $task.psobject.members.add($taskMethod)
  $task
}

Of course, you would need much more than this if you were going to build a real build system like Rake in PowerShell. For example, you’d need code to collect the tasks, order them in the correct dependency order, execute them, etc. Furthermore, Rake supports other types of operations, like file tasks and utilities that you’d need to build.

However, the point of this post isn’t to rebuild Rake in PS, but to show how PS rivals Ruby as a language for building internal DSLs. On that front, I think PowerShell performs beautifully.

I’m looking forward to using PowerShell’s metaprogramming capabilities often in the future.

Morning Doughnuts 3

  • What does it take to be an architect? Skyscrapr.net attempts to answer this question by asking a bunch of architects.
  • I have started teaching my children about astronomy. I found an open source product called Stellarium that is excellent for learning about the celestial objects visible in your area.
  • A Methodology for SOA adoption? I read an interesting blog on this subject from a couple of weeks ago. It’s not a long article, but the author makes some interesting points including an outline for SOA adoption.
  • I finally picked up Gears of War on Friday. It really isn’t a game I can see playing much, although I can see why it’s popular. I guess the best and the worst part of the game is having to utilize cover so you don’t die right away.
  • Windows Live Writer is a great tool! I use it to author the blogs for my website, and this week I have been using it on these Morning Doughnuts posts. My favorite feature is that you can preview your post and see exactly how it will appear on your website. This has been particularly useful since Devhawk and my site look quite different.

Reliably Beating a Dead Horse

(Harry is on a secret mission in uncharted space this week, so instead of the daily Morning Coffee post, you get a series of autoposted essays. This post builds on Harry’s recent epiphany about WCF and long running services)

Way, way, way back in March of 2003, IBM and Microsoft published an “overview and roadmap” white paper entitled “Reliable Message Delivery in a Web Services World“. It contained the following paragraph under the section “Exchanging Messages Reliably”:

WS-ReliableMessaging is not bound to underlying transport protocols or sessions. This means that the lifetime of a WS-ReliableMessaging conversation can span long periods of time (days, weeks) even when one or both systems are rebooted. This allows conversations to be suspended mid-stream (for example, to allow system maintenance) and then resumed without needing to retransmit the entire conversation. [emphasis added]

Now I know how I got confused about WCF and long running services in the first place. Support for long running services was part of the original web services vision!

About three years after that white paper was published, Shy Cohen wrote a post entitled Reliable Messaging Demystified on his blog. Shy was at one time the feature owner of WS-RM in WCF (according to his post) and wrote the following:

Reliable sessions [in WCF] are implemented using the WS-ReliableMessaging protocol. This protocol is yet another misnamed WS-* protocol, as it actually only deals with the reliability of the transfer and says nothing about durability, delivery acknowledgments, TTL for a message, long running sessions where a particular message is lost forever, etc.

At some point in the three years between March 2003 and February 2006, WS-RM went from being the enabler of long running services to “yet another misnamed WS-* protocol”. And with it, WCF lost (never had?) the ability to support long running services (as I’ve written previously).

Now all and all, this isn’t a big deal. I agree with Shy that WS-RM is under specified as mechanism for durable messaging (Shy calls this “queued messaging”). Attempting to build durable messaging on top of WS-RM sounds like it would have been both difficult and unlike to broadly interoperate. So implementing WS-RM for TCP style reliability and leveraging MSMQ as a transport for people that need durable messaging sounds like a pretty good compromise, especially for a v1 product. Of course, it is not exactly unheard of for a project’s end result not to completely live up to the original vision. But I have a specific requirements in this case, so I wanted to know more.

By calling it “misnamed”, it sounds like WS-RM was never really intended to be used for durable messaging. However, the July 2003 Reliable Messaging Feedback Workshop indicates that it was. In particular, Rodney Limprecht’s “Reliable Messaging Scenarios” deck describes WS-RM as supporting scenarios requiring “either volatile or durable endpoint state”. His list of scenarios included both an “Intermittent Connectivity” scenario where “messages pending transfer are staged to disk and exchanged when connected” as well as a “Message Queue Integration” scenario that used WS-RM to interop between JMS and MSMQ. Seems safe to say that WS-RM was originally intended to support durable messaging. So what happened? How did it become “misnamed”?

Rodney’s deck describes WS-RM as having the “flexibility to meet scenario requirements”. But flexibility comes at a cost. For example, the flexibility of WCF’s configuration comes at the cost of significant complexity. In the case of WS-RM, it appears that by trying to make it flexible enough to support both volatile and durable reliability, the authors might have made it too flexible. WS-RM implementers have broad latitude in building the capabilities Shy mentions (durability, acknowledgements, TTL, etc) as well as describing said capabilities in policy. By providing that latitude, we lost the ability to broadly interop durable messaging, which I would suspect is why it ended up out of scope for WCF v1.

As I said before, lack of support for WS-RM based durable messaging isn’t that big a deal. As long as you understand WCF’s sweet spot - the current version’s sweet spot anyway – and don’t try and make it be something it’s not, you should be fine. Furthermore, Shy mentions the need for an “interoperable Queued Messaging specification” and wrote that it’s something he “expect[s] that we will get to it in the near future”. Here’s hoping that spec is less flexible than WS-ReliableMessaging.

Morning Doughnuts 2

  • Joel Dehlin, the CIO of the LDS church has an interesting blog entry on buy versus build this morning. His main point is that buying is often cheaper, but only if you can move your business processes to match the processes in the off-the-shelf software.
  • The search for Jim Gray by his friends and colleagues has been called off. Even with a massive high-tech effort no new clues have been turned up. For the sake of his family I do hope that the mystery is solved. I would imagine it is very hard to not know what happened to him.
  • I am currently running a Build and Deployment Task Force. We are trying to ensure that our team follows best practices when building new applications. The project that Harry and I are working on seems to be a good test bed for the process.
  • For those of you who read my blog you know I am passionate about how we implement Service-Oriented Architecture in the real world. I have been reading a book titled Service-Oriented Architecture: A Planning and Implementation Guide for Business and Technology. I find the description of real business objections, and how to solve them quite refreshing.
  • It appears that the San Diego Chargers are going to hire Norv Turner to replace Marty Schottenheimer as their head coach. I don’t see how fans of the Chargers can possibly see this as an improvement.

Morning Coffee 33

I realize yesterday I said I was on vacation starting today. In reality, I’m not going to the office today, but I have time to post this before my vacation starts in earnest.

  • I hit Zero Email Bounce in advance of my vacation. It’s been quite a while since the last time I got here and I hope to hit it much more often in the future.
  • The DSL tools team shipped a Designer Integration PowerToy that allows you to integrate models from multiple DSL designers into a single authoring tool. Gareth has more here.
  • Assorted PowerShell links: PowerShell Analyzer and PowerShellIDE. Both look interesting.
  • Personally, I like Notepad2 but apparently the only way to add a new syntax highlight scheme requires modifying the source code. Ugh. Anyone out there already added PS support to Notepad2? How about a suggestion for a simple text editor that supports extensible syntax highlighting?
  • Steve, Nick and Tomas all commented on my long running services WCF post. Tomas mentions Advanced Message Queuing Protocol (AMQP) which looks to be developing an open spec queuing system like MSMQ or MQ series. Interesting, but given the lack of involvement of the major MQ and DB vendors, I’m hard pressed to imagine this gaining any kind of critical mass.