Sunday, June 10, 2007

Keyword arguments in XML-RPC

This isn't the least bit novel, for example I know I've been using this trick for years, but nonetheless here is a way to simulate named arguments in XML-RPC. XML-RPC only natively supports positional parameters, but by passing a single positional argument that is itself an XML-RPC struct (which is actually a mapping), you can simulate named and/or optional arguments. Rather than reproduce a sample XML-RPC document demonstrating this usage, I'll refer you to one of my earlier posts that utilized this technique; you'll see that the method is called with two named parameters: path and args.

If you are familiar with perl, you may also be aware of the trick perl 5, which also only natively supports positional arguments, uses to simulate named parameters. In perl 5, it is common to pass a hash of name/value pairs as arguments. However, what perl actually does under the scenes, and which is different from this XML-RPC trick, is to serialize the hash into an array of alternating names and values; it then passes this array as the positional argument list for the subroutine being called. The called subroutine then de-serializes the name and value pairs from the argument array, reconstructing the original hash. This flattening of a hash has to be a documented protocol between the subroutine and its callers.

Of course, you could do exactly the same thing using XML-RPC: serialize the argument dictionary into an array of alternating names and values and populate the method's param list with this array's elements. The XML-RPC server method could then reconstruct the original dictionary from the param list.

But XML-RPC also supports passing dictionaries and dictionaries: using the struct data type. Hence my original suggestion. Since to support named (or generic optional arguments) we have to document a protocol between the caller and the method, we might as well make the protocol as straightforward as possible. Rather than serialize and deserialize a dictionary of named arguments, just pass the dictionary as-is, as the one and only positional argument.

1 comment:

jjinux said...

As you know, Python has great support for keyword arguments, which makes me very happy. Apparently, Common Lisp did as well, although I'm not sure if it was the first. Ruby on the other hand, will automatically take a method call like "f(1, :a => 'b')" and translate the ":a => 'b'" part into a hash. On the surface, this is sufficient, but experience shows it's not as nice as the Python approach, especially if you happen to misspell the key ":a". In Python, you'll get an exception. In Ruby, you'll get a bug ;)