A journal to record my notes and ideas related to software development and computing

Sunday, February 14, 2010

Comment on previous post

I received a comment from a past work colleague of mine via Buzz in response to my previous post about my experiences with dynamically typed languages. Unfortunately, what I wanted to write didn't fit in the blog comments, hence the new post:

Murray Spork - So if I comment here I guess the comment only appears in Buzz - or does buzz know how to post this to your blog?

Looks like it's not that smart yet...

My thoughts:

1. I think you are correct in identifying the main problem of dynamic languages - what has been call "contract obfuscation"

I think it's more the fact that it is not a system that allows contracts to be automatically checked.

2. Test driven dev is essential with dynamic languages (well - I would say it is essential even when using static languages ;-). I feel your pain regarding maintaining the existing Rails app - but if they had better test coverage that would have gone a long way to solve that problem

It depends how coverage is determined. As I mentioned in the previous post, this particular code base had over 98% coverage, but it didn't take into account multiple code paths on a single line.

It's also not as effective as it's made out to be. Imagine I had some sort of address property on a User. I write unit tests around the User class based on the internal meaning of the property. Then, I write tests around all code that accesses the address property. I do that using a mock framework:

mockUser.expects(address).andReturns("1 Park Road")
proxy = new Proxy(mockUser)
assertThat(proxy.address).equals("1 Park Road")

Now, three months down the track, we find out that a user can have multiple addresses, and we store them as a map (:residential -> "1 Park Road", :postal -> "PO Box 132"), under a new property called addresses.

So the new programmer on the team goes in and updates the User class (or should I say, updates the tests around the User class first, then updates the User) so that it uses the new "addresses" property. And hey presto, all the tests pass. Time to move on, right? Well, no, there's still that bit of test code above - the one that still works, because the proxy still calls the "address" method, and the mock object is setup to expect a call to address.

This is a trivial example of a problem I came across many times during maintenance. Of course, some people would say that integration testing would catch the problem, but isn't what we really want to test the fact that the property exists and returns something of a particular type? Doesn't that sound like a good job for a compiler?

So much unit/integration testing I've seen on dynamically typed code is based on testing the type contracts, not the business logic. It seems like such a wasted effort to me.

3. "in 5 years time static compilation will be seen as a weak form of unit testing" - forget who said that - provocative but some truth to it

I would put it the other way - test-driven development is a weak form of typing. Look at the Scala code in the previous post. What test can I possibly write to ensure that resetSystem must be called with a User that has already been authorised?

Or another example; what tests and defensive coding practices would you use around a list data structure that was assumed to always have at least one element in it? Using a static type system, I can write a data structure (or even better, use one from the standard library) that enforces this constraint. It would just not be possible to create an instance of this list with no elements in it (compiler error), and so any client code that ended up with an instance of one of these lists can call get(0) without the need to worry about the list being empty.

4. metaprogramming should of course be used with utmost caution - rails is horrible in this respect - they went crazy with "monkey patching" for the sake of it - even when there we must simpler solutions. Pitty that Merb got merged into Rails - they were doing a much better job in this respect.

Monkey patching is a great concept that has a terrible name because of dynamically typed languages. Imagine if you could do it in a scoped way that is guaranteed by the compiler not to conflict with other "patching" on the same class. Scala has exactly that feature.

4. did you know that the automated refactoring came from the smalltalk world (Ralph Johnson)? Don't confuse immaturity of ruby tooling for some fundamental language weakness.

And was the refactoring 100% automated and effective, or did it require some human intervention or produce any false positives? It's been too long since I've programmed in SmallTalk, but I'm pretty sure I could think of cases that would be missed.

5. IMO the only argument in favor of static languages is performance. However I'm a self-confessed dynamic language bigot - and as one static language bigot confessed to me: ultimately it is probably just a matter of style - something to do with how our brains are wired differently as to which we prefer.

Before you consider what I'm about to say, bear in mind that I'm not talking about Java or C#. Both of those languages have a terrible implementation of static typing that more often that not gets in the way of what you're trying to do.

So what do you consider the advantages of dynamic languages? Is it terseness? If so, I'll show you some Haskell. It certainly can't be correctness, as static typing is an encoding of a mathematical theorem; something that you can use to prove correctness (actual proofs, not failure to disprove, aka unit testing). Given the amount of additional effort of writing tests to reproduce what the compiler can do, I can't imagine that efficiency would be a benefit. Lower barrier of entry? Well, yes, I think that's the big advantage of dynamic languages. But is that something you really want to strive for?

About Me

Computer programmer with and interest in music, and a passion for brewing beer, which I'm working at developing into a career!