Back when I was giving my Ruby.MN and Groovy.MN* presentations on OCaml (blog post with video and handouts), one of the questions that came up (from Jesse of Refactr) was why I saw static typing and functional programming as being so tightly connected. After all, Erlang is a dynamically typed functional language, so what’s with the whole static typing hang-up?
At the time, I had an answer, but it isn’t the whole story. That answer was that dynamic typing doesn’t get rid of types, it just moves the type checks runtime (see Ruby’s “No such method” exception/”nil when you didn’t expect it” exception), you still have to be wrestling with type information in your code. When you start working at the higher level of abstraction that is functional programming, tracking types “in your head” becomes tricky, and so static typing helps identify bugs.
One of the big examples is point-free programming, which simple implementations of can be done in Groovy by chaining together functional operators.
results*.foo.collect { [it.bar, it.size() / 2] }.sort({ a,b -> a[1] <=> b[1] }).findAll { it[1] > 0 || it[0].name == "EMPTY" }.collect { [it[0].baz, it[1]] }.inject([:]) { hash,entry -> log.debug("Processing ${entry[0]}") hash[entry[0]] = entry[1] }
And so on. As you build up these kinds of constructs, it becomes easy to lose track of what you’re doing and where you’re at — they’re maintenance nightmares in a dynamically typed language, but they’re wonderfully succinct ways of expressing business processes. The dynamic typing situation gets even worse if you try to use closures which aren’t defined straight in-line, or which had previous processing (e.g. curry) done on them.
There’s another reason, though, which I’ve just recently bumped into while developing the BackgroundThread plugin for Grails. Consider this scenario.
let foo = "foo";; let x = (^) foo;;
The same thing (within handwave) in Groovy is:
def foo = "foo" def x = { foo + it }
So far, no big deal. Here’s a key difference, though — in Groovy, you could define x this way:
def x = { "$foo" + it } // Stringify foo
Or, if “foo” was a property on the owner:
def x = { owner."$foo" + it } // Access "foo" property in enclosing context
So, in Groovy, when you define a closure, you’re required to hold onto the entire surrounding context — the garbage collector won’t clean up any of those variables, because they may happen to be used. In the OCaml code, the only thing being stored with the closure is the “foo” variable itself, because OCaml knows you won’t be able to use anything outside of it. This difference becomes extremely relevant when you start dealing with long-lived closures (like in the BackgroundThread plugin), because a context that throws off a number of background tasks won’t be cleaned up until after all of those tasks have resolved themselves. And if you’ve got a list of 3000 domain objects in that context, that’s a non-trivial amount of memory you’re hanging onto.
So, if you’re going to be throwing off a closure which may be long-lived in a dynamically typed language, it is important that you try to use consume as much of the context as you can to minimize your memory footprint and allow the garbage collector to do its thing.
*BTW: I’m bummed that the .NC domain name registrar requires you to live in New Caledonia. So much for my dreams of Groovy.NC…
Related posts: