Get Argumentative with Javascript

When I looked at the Google Maps API for Javascript, I couldn't help but notice that all the functions in the API Reference look kinda Pythonic. Functions call be call with a positional argument list like normal, i.e.:

new GMap2(mynode, myopts)

or by name with an object literal:

new GMap2({node: mynode, opts: myopts})

Moreover, arguments can be optional, and they are denoted in the doc with a ? at the end. Finally, the type of each argument is specified.

Here is an example of their API:

GMap2(container:Node, opts?:GMapOptions)

I thought: How cool is this!!?? I want this!! But, as the Maps API is proprietary, I couldn't get hold of their source. So, it was time for a little hacking...

My immediate goals were: argument validation, named argument passing, and optional arguments. Now I have a prototype with these three things working.

Argument Validation

Argument validation in my terms means checking the input arguments to a function when it is called to see if they are valid. The most basic of these checks is whether the number of arguments is correct. Example, we start with a simple add function:

function add(one, other){
  return one + other;
}

Then we enhance it by wrapping in with the $f function:

add = $f(add);

The new enhanced add function now will require the argument list to match up with what is expected:

> add(1,1)
  2
> add(1)
  Error: Argument 'other' of function add(one, other) was not specified.
> add(1,2,3)
  Error: function add(one, other) expected 2 arguments but got 3: (1, 2, 3).
Named Argument Passing

Named argument passing is something that Python programmers enjoy, but in Javascript you can simulate it using map literals. For example, given the add function defined above, you can now also write:

> add({one: 1, other: 2});
  3

The order does not matter because it is a map, so the above is equivalent to:

> add({other: 2, one: 1});

  3

You can also mix positional and named arguments just like in Python:

> add(1, {other: 2});
  3

Optional Arguments

Optional arguments is another great Python feature. It is really the feature that makes named argument passing worthwhile. In Python we have a nice syntax to specify the default value for an optional argument:

def f(x=0):
  return x * 2

In Javascript, not so lucky. I had to resort to this:

f = $f(
  {x: 0},
  function f(x){
    return x * 2;
  }
)

Not so bad? Maybe? I am trying to make it look like a decorator/annotation. So, with this code, you can do:

> f()
  0

Just what you'd expect.

The combination of named argument passing and optional arguments enable very concise code in some situations. Example, let's say you have a function that generates an html input element given a bunch of parameters:

function input(type, name, value, classes){
  return '<input \
    type="' + type + '" \
    name="' + name + '" \
    value="' + value + '" \
    class="' + classes.join(' ') + '">';
}

In this case, type and name are required. Value is required by the spec but we can just default it to ''. Classes is optional and we'll just default it to [] to avoid a check for null.

So the code to do that is:

input = $f({value: '', classes: []}, input)

And now you call call the function in these variety of ways:

> input('text', 'age')
  <input     type="text"     name="age"     value=""     class="">
> input({type: 'text', name: 'age'})
  <input     type="text"     name="age"     value=""     class="">
> input({type: 'text', name: 'age', classes:['text', 'age']})
  <input     type="text"     name="age"     value=""     class="text age">

Nice!

The code for this prototype is as usual on github. The next on my todo list is to implement variable length argument list and extra keyword argument lists. So stay tuned.

blog comments powered by Disqus