Thursday, May 10, 2007

Static Typing & Bureaucracy Redux

As I expected, there is serious dissension in the ranks over my post claiming that "static typing is communist bureaucracy". Some of the areas worth rebutting:

  • Static typing helps tools

    Yes, it does (although dynamic typing doesn't preclude tool support -- Smalltalk had the one of the first refactoring tools built in, and NetBeans is adding some pretty good support for JRuby). This is a problem that will be solved. However, I reverse this question: tools make static languages merely palatable. Iterating over collections in Java or C# is ridiculously painful. Using the each method with a closure in Ruby or Groovy is a joy. Tool support in languages cover up for deficiencies in languages and libraries. Graeme Rocher (creator of the Grails framework) stated this quite elegantly in the GroovyOne panel I was on, stating that you don't need as much tool support in well designed languages because they tend to be more consistent (what Martin Fowler calls "Humane Interfaces").

    Granted, tooling makes things easier: no question there. But, are you willing to give up the flexibility of a dynamically typed language just to get better tool support? In my aboutGroovy podcast, I said (and meant it) that I would rather write Groovy in VI than Java in IntelliJ (and I think my position on IntelliJ is pretty clear).

  • Static analysis tools are useful.

    Yes, indeed, anything you can find with an automated tool is low-hanging fruit. But, again, it's not a pure trade-off. You can do some of the same kinds of things in dynamic languages (although clearly not as much), but I would still rather have the flexibility of the dynamic language plus tests to verify my code. In the perfect world, I'd have the flexibility and power of the dynamic language and the tooling possible and available for static ones. This is one of the places that Groovy comes in handy: if your static analysis tool works off byte code (like FindBugs), you can run it on a Groovy codebase.

  • Static typing creates better contracts and calling semantics for libraries.

    Dynamic languages tend to eschew strong contracts like interfaces. What I've found it that it forces developers to create much better method and parameter names, because that becomes the primary way that you consume methods. When done right, this is an effective way to communicate. Granted, this is not universally the case even in the Ruby libraries, but removing props sometimes forces people to make better use of what's left. The best example is the town of Drachten, in the Netherlands, who removed all their traffic signs and lights, forcing people to interact in the intersections. It made their streets much safer because they removed signage.

Sure, in a perfect world, all the facilities that we lean on in statically typed languages would be available in dynamic languages. But they're not. And, having worked extensively in both static and dynamic languages, I prefer the flexibility and expressiveness of the dynamic languages currently extant to the static ones, even with all the tool support. Still, testing is the most important verifier of code. I think it is professionally irresponsible to write code that isn't tested. If you are going to write that level of testing anyway (especially if you do it test-first), a dynamic language allows you to get more done in less time with a lot less typing (a lot of which is required to make static typing work).


Martin Cron said...

I know I'm out-of-touch when it comes to modern dynamically typed languages (although I used to use and love Perl back when it was a modern language). You're clearly out of touch when it comes to modern refactoring tools.

"Iterating over collections in Java or C# is ridiculously painful."

That's just wrong. With ReSharper, I can type "foreach", and hit the tab key it will automatically pick up all variables in the context that implement IEnumerable, selecting the most recently defined one first (which is usually the one I want). Hit enter to have it take care of the rest of the foreach statement, automatically generating a meaningful name for the variable to use in the loop.

Now I've got readable code for type-safe iteration over the collection which isn't coupled to the kind implementation of the collection. It took what, nine keystrokes? I don't think it's fair to call that "ridiculously painful".

But, I'll admit that the rest of the argument is compelling, and it renews my desire to try coding in Ruby to see why so many smart people like it so much.

Neal Ford said...

But I think you've helped my argument! Tools make language palatable. But you still have to read all that stuff. 9 keystrokes? .each{ |e|} is 10. But to read the code, you still have to slog through multiple lines of code, vs. the simpler, more elegant .each on all types of collections (the semantics of which are all different in Java and C#). This is similar to the argument that you can fake closures in Java with anonymous inner classes. And many of the people who came from languages with closures tried, but the hideous syntax drove them away. Syntax is important not just to write, but to read as well.

Tools make all the difference in static languages. That's why I can't even use Visual Studio without ReSharper. But I don't find that I miss that class of tools as much as I thought I would in Ruby and Groovy.

Tetsuo said...

Counter-argument 1 wasn't about static vs. dynamic, but about humane vs. minimal. Static typing precludes humane interfaces as much as dynamic typing precludes tool support, maybe less. Closures, type inference, humane interfaces... all this are perfectly possible in static languages. Just because Java/C# weren't designed with them doesn't mean that another language (and another API) couldn't be.

Counter-argument 2 was pure opinion. You prefer dynamic typing's flexibility. Well, I don't usually use static analysis tools, so I can't say much on this...

Counter-argument 3 was limited in its view. It assumes that all developers are ThoughWorkers. It's a fact that any language, when in the mainstream, gets 'degenerated'. See VB. See PHP. See Java. Good developers will write good code in any language they are proficient at. Maybe Ruby code's average quality is better than Java's, but that is just because it has not hit the mainstream yet. The ones that use it are the ones who tend to be pioneers, who like learning by nature. Any language whose developers are like this in their majority, tend to produce better code. But, after some time, hypes produce much more lazy-noobs than specialists.

I'm not against testing in any way but, most code out there isn't tested. Much of it is tested only in production. Right or wrong is not the question, it's just reality. Most code isn't written by experienced developers, but by code-monkeys. Static typing at least forces a minimum 'implicit' testing (and doesn't preclude 'explicit' testing).

Greg Buchholz said...

Maybe you'd like to give a language
like Haskell a try.

navision said...

Tools make static languages palatable. I can buy that. I'd also have to say that convention, language design, and library design in Ruby make dynamic typing palatable. .each is a good example. There is no reason such a thing could not be possible in a statically-typed language. C# is working in that direction with type inference, etc. The JCP is definitely holding Java back in this regard.

I agree with you that Java and C# are far too verbose at this time (and because of that evil backwards compatibility thing may never be able to fix this), but you phrase it as static vs. dynamic.

Obviously static typing requires the verbosity of declaring your types when creating classes, methods, etc. I'm not sure that the verbosity needs to extend beyond that, however. Since tools clearly benefit from having that type information, and programmers clearly benefit from having powerful tools, I'd settle for that small amount of extra declaration.

I think Ruby supports Graeme's statement beautifully as people are quite productive in Ruby without good tool support. However, 'need' is an extreme word. To say they wouldn't greatly benefit from good tool support is clearly ludicrous. You claim that the problem will be solved. I can't say that I've made the jump to sharing your confidence. On the one hand, Smalltalk solved it because it had a lot of smart people working on it. It also had everything stored in an image and thus much easier to manipulate without side effects. On the other, Smalltalk had the benefit of several well-funded corporations competing for mind share and a load of cash. The JetBrains tools (and even irb, I suppose) take some steps to realize the advantages of the Smalltalk image in other ways, so there is probably hope.

Static typing does not preclude tests, humane interfaces, or appropriate naming (this is even a problem in the verbosely_named_method_ruby_world!) It is a problem that can be addressed by training (talk to Fred George about that one).

I think removing props is a great experiment and may work well on a small scale in a tight community. Imagine removing all of the street signs and traffic lights in NYC (where people already take everything they can get), though. In Drachten, I may be able to count on others to do the right thing on the road more often than not. In NYC, that trust would soon lead to death.

The work going on the Ruby space is great, don't get me wrong. It is bringing back some of the ideals that were largely left behind with Smalltalk. Smalltalk shows a lot of what is possible in a dynamic language (probably even better than Ruby does in many ways), but where is our great static example? I don't think we've gotten there yet, though IntelliJ and other Java tools do provide a glimpse of what is possible. As a first exercise, now that the core Java libraries are mostly GPLd, a community of like-minded people could use those as a base to providing Java WITH humane interfaces. Later, a compatibility layer could be mapped by wrapping the humane interfaces with the current inhumane ones. This could only go so far without language enhancements, but if the JCP doesn't do what is necessary, the tools necessary to fix this are also largely available.

Petr Panuška said...

I'm not sure how .each is related to dynamic languages. I believe, it is just a matter of syntax and we can have an argument about the syntax of Java, C# or Ruby but it has nothing to do with dynamic or static typing.

But, are you willing to give up the flexibility of a dynamically typed language just to get better tool support?
Will a blacksmith stop doing his job if he were not allowed to use a hammer? Would he stop hammering without a hammer?
I hope that most of people today understand how tools are important in a daily life. Obviously, we can make products just with our hands but how mature the products will be? Yes - tools determine our jobs in these days.

The analogy with the Dutch town - Drachten and the traffic experiment in its city centre reminds me the analogy of small (perhaps prototypes) projects and big, seriously taken projects. Yes, I agree, for protyping is not the static typing neccessary. For larger projects I doubt about it.

David said...

Also, static analysis just means analysis done by looking at the source when it isn't running. Smalltalk has tools (sLint) for static analysis. As you point out, it is a system of trade offs. Static typing makes static analysis and refactoring easier, but it hurts readability and other things.

Daniel Yokomizo said...

Trying to compare static vs. dynamic using Java or C# as the pinnacle of static typing should using Basic as the pinnacle of dynamic. A better comparison should match Ruby against Scala, Python against Haskell, etc., so we at least have decent languages on both sides. Also your third point analogy is wrong, with static types you can't do incorrect operations (e.g. use a string where an integer is expected), traffic signs provide no guarantee, they're just conventions that people may follow. More like urging the caller to take certain locks before calling a function, calling some methods in a particular order, etc., things that (usually) can't be verified by the compiler. If we go through this particular analogy, tests also just show that particular drivers in a lab environment watched for the crossroads and never crashed, but don't say a thing about drivers in the real world.