Monday, August 16, 2004

Dynamic Freedom Hoedown

Well, James Robertson's blog has been full of activity on the whole dynamic vs. static typing debate. But, I was reading one comment and it really bothered me. Read the fun here and here So, I thought I would post on it.
    Comment from: Darren Oakey on '2004-08-15T22:28:25-05:00'
    I read Peter's post, but with no obvious way to submit a comment on that page, I have come back here.  I'll start first stating my amusement at the "you're wrong because.. well.. I say so" approach to argument :)

    Anyway.  Most of the post is devoted to pointing out that you can do type-safety in Smalltalk.  Gee..  Who cares?  I'm not attacking Smalltalk  I think it has a lot of good things about it, which is why I read Smalltalk blogs.   For that matter, in more traditional languages, you can have complete dynamic types.  You can just declare something as an "object".

    The argument that I put forwards though was that (please think outside the confines of any particular language) - the conceptual mode of programming towards a specific locked type is SAFER and therefore BETTER.   i.e. - a software program made with a higher level of type safety will have less bugs than a software program without - that's all I'm saying - and that concept wasn't addressed at all.
    "oh but I've done a big software project without type safety". Well done.  Congratulations. 
    Doesn't say whether it was a good idea

Dynamic typing is a good idea. I've worked on several projects in the past (Smalltalk, Java, C++, etc). And by and large, the Smalltalk projects have been less error prone. Now, the interesting question is why. And I think it has NOTHING to do with type checking! It has everything to do with the environment. You see, you are constantly running code in a Smalltalk environment and you never let any chunk of code go un-ran for any length of time because you are always checking your work. In Java and C++, you do this less because of the whole code-compile-run cycle. The compile cycle is implicit and incremental in the Smalltalk system. Plus, the type checking system hinders more than it helps. You are typing in a lot of information without getting any benefit. I find I can change my designs quicker and more safely with dynamic typing and TDD. In Java and C++, you have to get the design upfront because it is so hard to change it down the road. The funny part is a design always changes because your understanding changes during the process and it's great for the design to reflect your new found knowledge. I know I am not the only person with the problem. Just read Martin Fowler ("Reefactoring"), Kent Beck ("TDD"), and countless other better than I.

    I will make some more assertions - if you disagree with these, there is no point in discussing anything, because I see these as "proven" statements.  They are part of the frames of reference, not part of the argument...

Isn't the point of arguing to argue? NOTHING HAS BEEN PROVEN. Our science is still too new to have hard and fast rules that need no arguing in fact, if we stop, we might as well just give up on improving. I want to keep improving, so I am going to argue where I disagree. Thank you very much.

    1> edit time checking is superior to compile time checking is superior to runtime checking.  Just read any McConnell book, or look up ANY metrics on programming.  The cost of a bug or error goes up exponentially the later it is detected.  It's a documented fact.

I think you're confusing when a bug is expensive. I see edit, compile, and runtime as all development activities. I agree bugs are more expensive as THE PROJECT PROGRESSES. And the worst kind of bugs are requirement bugs and those are the expensive ones because they generally require design changes. Smalltalk handles design changes very well while Java/C++/etc doesn't because of the all typing information that gets in the way of the redesign. Besides in Smalltalk, there is no distinction betwen edit/compile/run times. It's all together.

    2> Safer = better.  I don't know about you, but I am a senior architect / developer in the real world.  Many of the programmers I work with wouldn't be able to spell encapsulation, let alone explain what it means.  If, when I build the core libraries people use, I give someone a way of doing something stupid... rest assured - they will..

What do you know! I'm a senior architect / developer in the real world too! What's your point? Does the title give you more validity? I think in our industry titles are thrown around way too much to prove points. Anyway, I digress. I think our job as more experienced developers is to educate the less experienced developers. If they don't know how to spell encapsulation TEACH IT TO THEM! If you never show them the way, they will always be lost. They will always be ruining your "perfect" designs. TEACH! It's your duty!

    So - back to the core discussion.  Is it (conceptually) better to code with type safety, or better not to.  Of course, practically, we all use both.  For instance if I made a collection class - I'd make the base collection class with "objects", and then specialize it for each class I actually use.    Of course something like templates/generics give us the best of both worlds - and strongly typed functional languages like Miranda, with pattern matching go even better... but lets look at the extremes - if you are forced to make a choice, which way should you go?

I've worked both sides and I chose. I choose Smalltalk. Not only for the dynamic typing but for its superior environment.

    Now - to argue the dynamic type problem, I'll throw out two statements which I'll then support - those statements together logically demonstrate the need for type safety:  a) dynamic types are only safe if you have a clear and complete understanding of the objects you are working with  and b) in a large system with a lot of programmers of various skills and experience, it is impossible to have a clear and complete understanding of the objects you are working with.

If you don't have a clear understanding of all the objects in your system, then you do a poor job of design. You didn't split the system up into the right modules. You don't need to know about every object, but you should know how the modules fit together and how the objects in the modules you work on work. I think you have the same problem whether you work in Smalltalk or Java or whatever. It's a good/bad design issue.

    So.. looking at a)  We've done the road example.  Let's go back to the train track example, and we can very easily see why even if you can, it is dangerous to do something that wasn't expected by the designers.  Suppose in the real world, we have two states, NSW and Queensland, that evolve their train system differently, and end up with different gauges.  They eventually decide to communicate, and we have a massive amount of trouble doing so.  We end up with all sorts of kludges to stop at the border, transfer cargo and so forth - and it ends up costing us millions and millions...   That's bad right?

    Well - suppose instead initially - they had both looked up the US standard.  And followed that.  The gauges are compatible.  Yay.. lots of saved money.  However... what we didn't know - and when they drove the trains on and started using the lines, no one thought to mention it, because it didn't occur to anyone - was that NSW early in development had had better suspension technology.  Over the years, the superior trains in NSW had allowed much tighter corners than you ever saw in Queensland.

    So.. What happens when the press all gets ready and watches for that first passenger train to come screaming down the lines from Queensland?  Yes.  That's right.  The train derails, and hundreds of people die.

    A contrived example, sure - but it applies to every part of the system.  If I pass a control object to a graphics collection, I EXPECT, because I'm guaranteed - that that object will be able to draw itself.  There is zero possible benefit, and an enormous amount of possible danger of passing an object that can't draw itself to a function that draws all the objects in the collection.    While we can say  "oh yeah, and this expects objects that behaves as follows" - why shouldn't we ENFORCE that?  Even though it might be slightly more work, it is guaranteed to be superior in 100% of cases.  

Repeat after me, TDD, TDD, TDD. Keep saying it because EVERY system needs to do it. Without, enforcing all the types in the world will not fix bugs. Most of the bugs I run into are not interface problems, but internal bugs in the objects. The interface bugs are the easy to fix and no brainer bugs. The harder ones are in which there is an algorthm problem in the drawing of a component on the screen (taking from you example of course).

    And, going to statement b) the reason it is necessary is because in the real world, you have no idea who will next use the class that you've just written.  They could be a complete idiot, they certainly won't understand all the assumptions you've made and the thinking you've done to produce the class - and won't have the time to read all (or any of) the documentation you've provided with it.  All they see is a function call that they have to send values to.

If you always assume the users of your code will be idiots then the code will be unusable by smart folks as well as idiots. I always think that the people that will be using my system will be smarter than me. I do not want to shackle them in anyway because I want them to be to adapt to circumstances that I never could have imagined when I wrote the code. I firmly believe this because Swing was built assuming the users would be idiots and it's the worst case of design I have ever seen. It just reeks bad design and part of that is because of developers assumed the developers were idiots. Never think of your fellow comrades like that, it's rude and not true. If you think they are idiots, it means they are not as educated as you. Well, your job is to educate them, not protect them.

    The greater restrictions, guards, pre/post conditions etc you can place on that function, the BETTER the resulting system will be.  ALWAYS.  Because the average programmer is a complete idiot 

The average programmer IS NOT AN IDIOT. How dare you! The average developer is a smart person and it's impossible to know everything about computer science. If your developers don't get your designs, then maybe your designs are poor or maybe you need to educate them with what you know. I spend most of time educating and an architect that ridicules and not teaches is not an architect IMHO. It is your job to teach. My dream is to teach what I know so someone will have the time to learn something new to teach me.

    I think one problem that you are facing understanding this is that you are dealing mostly with what is a very niche language.  Your average Smalltalk programmer will be a fairly serious, hardcore developer - because the language itself is not very accessible.  You are not daily dealing with people who's entire programming experience is that 4 day introduction to VB course..   But that's the sort of person who ends up using my libraries, and I need every bit of safety I can get my hands on.

You would be amazed. At the company I work at, they come from all walks. Some of had no training and they are excellent developers in their own right. They all varying degrees of expertise and well, they all love to learn. So, yes, I am dealing with people that you say I am not. In fact, the quality of the Smalltalk code is better. Why? Because of the whole constant edit/run cycle that Smalltalk encourages And that's not a dynamic vs. static argument. It's an environment. Static vs. dynamic argument comes into play when we start talking changing designs. In that regard, dynamic always wins. We all need TDD to ensure our code works. There are no short cuts in that arena.

No comments:

Amazon