Dynamic languages are an anti-pattern

7 points by DanWaterworth ↗ HN
I'm starting to think that it's much more difficult to write maintainable dynamically-typed code than it is to write maintainable statically-typed code. Many of the bugs I've been fixing lately would have caused type errors in a statically typed language.

13 comments

[ 4.0 ms ] story [ 31.5 ms ] thread
I experienced the same, but its more the fault of the programmer. Dynamic languages are easier to enter ("learn") and thus attract more mediocre developers who deliver inferior code.
Which is why the importance of unit testing is underscored in the communities around dynamic languages.
Really? In my experience, it's the Java community that has the most focus on unit testing.
Compared to dynamic language communities? Not even close. Just look at the almost cult like devotion to writing tests in the Ruby community.
You had bugs because you didn't have adequate tests, not because you were using a dynamic language. That's the anti-pattern.
(comment deleted)
One could also view statically typed languages as having "built in testing", at machine compile time, of types which would be covered by human written tests in a dynamic language.

It is easy to say someone doesn't have adequate tests but much harder to make an "adequate" amount of tests in a dynamic language (1).

Given a choice, I would rather have a machine checking for correct usage of types as opposed to human written tests.

(1) Note I said tests and did not mention systems level behavior verification which is in-depended of a statically or dynamically typed languages.

The problem is that to find the most annoying of these bugs using testing alone would have meant that so much time would have been spent writing tests that the productivity gains that you get writing in a dynamic language would have been nullified.
IMHO it depends on the part of the project.

When you are building infrastructure (buisness logic independent), static languages are better, because most things are clearly defined, and change rarely, so overhead is small, and the code is general (few special cases).

When you build the real thing, that includes business logic, dynamic languages are better, because it's much easier to change business logic and add special cases on the fly. And there are always special cases.

That's why big projects written in static languages often have implementations of some dynamic language embedded, and when they don't, they use unholy mess of configuration files and dependency injection to achieve the same goal.

I much prefer intentionaly designed embedded dynamic language to writing implementations of factories and changing xml files.

To see how much dynamic your static code is count all the casts from Object, and all template classes instanced for Object (especially common are List<Object>, Map<String, Object>).

Writing infrastructure code in dynamic languages - I agree - it's harder to get right than with staticaly typed languages, and the benefits aren't obvious.

Thanks everyone, reading through your answers and thinking through the bugs I've solved has lead me to some interesting conclusions.

(The language I'm using is Ruby).

The problem is not that Ruby isn't statically typed. It's that what I think of as type errors in Ruby pass without incident. The most irritating example of this is that if you don't pass enough arguments to a proc object the other parameters get filled in with nil. In my mind this should throw a type error, but in Ruby it doesn't.

Perhaps this is why Rubyists are so keen on testing, because their language is full of implicit behaviours.

> The most irritating example of this is that if you don't pass enough arguments to a proc object the other parameters get filled in with nil. In my mind this should throw a type error, but in Ruby it doesn't.

Maybe automate checking this? I don't know Ruby much, but there's .arity method on proc that seems to be apropriate for this.

Proc doesn't check arity. That is just how proc and Proc.new are expected to work. If you want to check the arity, use lambda or ->.

x = ->(a, b) { puts a, b }

x.call #ArgumentError: wrong number of arguments (0 for 2)

x = lambda { |a, b| puts a, b }

x.("just one argument") #ArgumentError: wrong number of arguments (1 for 2)

Sorry, it wasn't a proc object, it was a block. The following is a potential fix, but it's irritating to have to write it.

  blk = lambda { |arg1, arg2|
    # do something
  }
  obj.some_method(&blk)