Monday, February 17, 2014

Keyword/Named arguments in programming languages that don't support it

Let's say you come from a scripting background like python or perl. Those languages have a cool feature called keyword arguments, which essentially allows you to pass a hash/dictionary of key/values to a function without declaring an object:
# python
def printArgs(*args, **kwargs):
print kwargs
>>> printArgs(a=1, b=2)
{'a': 1, 'b': 2}
# perl
use Data::Dumper;
sub printArgs {
my %args = @_;
print Dumper(\%args);
}
printArgs(a => 1, b => 2)
$VAR1 = {
'a' => 1,
'b' => 2
};
view raw kwargs_1 hosted with ❤ by GitHub
This language feature saves you the effort of creating an on the fly data structure to pass arguments to a function:
def printArgs(arg):
print args
>>> printArgs({'a': 1, 'b': 2})
{'a': 1, 'b': 2}
view raw kwargs_2 hosted with ❤ by GitHub
This is essentially what you have to do in javascript to accomplish keyword args:
function printArgs(args) {
console.log(args)
}
> printArgs({'a': 1, 'b': 2})
Object {a: 1, b: 2}
view raw kwargs_3 hosted with ❤ by GitHub
But what if you wanted to not have to always create an on the fly object? What other tools do we have available? Well, you could still use variable arguments:
function namedArgsFunc() {
var args = {};
for (var i = 0, len = arguments.length; i < len; i += 2) {
args[arguments[i]] = arguments[i+1];
}
console.log(args);
}
> namedArgsFunc('a', 1, 'b', 2)
Object {a: 1, b: 2}
view raw kwargs_4 hosted with ❤ by GitHub
So, knowing this, we could write a shim for javascript to parse named args with arguments:
function getNamedArgs(args, opt_start) {
opt_start = opt_start ? opt_start : 0;
var obj = {};
for (var i = opt_start, len = args.length; i < len; i += 2) {
obj[args[i]] = args[i+1];
}
return obj;
}
function namedArgsFunc(x) {
var args = getNamedArgs(arguments, 1);
console.log('x ->', x);
console.log('args ->', args);
}
> namedArgsFunc('x_variable', 'a', 1, 'b', 2)
x -> x_variable
args -> Object {a: 1, b: 2}
view raw kwargs_5 hosted with ❤ by GitHub
You may be thinking, "Big deal, I don't need to use arguments, I can just pass on the fly objects in javascript thank you very much."
But this example was really a fake out. You can actually use this example with a non-scripting language, like Java, since they also allow you to use variable length arguments:
public class Utils {
public static Map<String,Object> getNamedArgs(Object... args) {
Map<String,Object> obj = new HashMap<String,Object>();
for (int i = 0, i < args.length; i += 2) {
obj.put((String)args[i], args[i+1]);
}
return obj;
}
}
//...
// then you can take any variable argument function and slap this on:
public void doSomething(Object... args) {
HashMap<String,Object> namedArgs = Utils.getNamedArgs(args);
// ...
}
view raw kwargs_6 hosted with ❤ by GitHub
Why would you want something like this in java? Well, lets say you want to create a function that creates a test object for a junit test. The object is essentially a POJO, but you don't want to write code like this:
@Test
public void testSomething() {
MyObj testobj = new MyObj();
testobj.setFoo('a');
testobj.setBar(123);
// ...
testobj.setBaz('z');
}
view raw kwargs_7 hosted with ❤ by GitHub
Wouldn't it be better if you could write it like this?
@Test
public void testSomething() {
MyObj testobj = TestUtils.getTestObject(MyObj.class, 'foo', 'a', 'bar', 123, 'baz', 'z');
}
view raw kwargs_8 hosted with ❤ by GitHub
Then all you need to do is create the getTestObject method. One caveat is that you can either lookup the public setters by name, or you have to expose the private variables. I am for exposing private variables since we are in test land. You probably shouldn't change field access control in production code.
public class TestUtils {
public static <T> T getTestObject(Class<T> type, Object... args) {
Map<String,Object> namedArgs = Utils.getNamedArgs(args);
Constructor <T> constructor = type.getConstructor();
T testobj = constructor.newInstance();
for (Entry<String,Object> entry : namedArgs.getEntrySet()) {
Field field = type.getDeclaredField(entry.getKey());
if (field != null) {
if (Modifier.isPrivate(field.getModifiers()) {
field.setAccessible(true); // this is only for test code
}
field.set(testobj, entry.getValue());
}
}
return testobj;
}
}
view raw kwargs_9 hosted with ❤ by GitHub
So with a little bit of utility code, you can create static constructor functions that use variable length Object arguments to simulate named arguments. I'm sure there are Java purists that will say this violates object oriented programming principles, but I argue that this is just for test code and is just a means to an end of creating on-the-fly objects with less typing/effort.

No comments:

Post a Comment