Summary
Musings on the reasons people like me prefer static type checking for building systems.
Advertisement
In his weblog entry, Are Dynamic Languages Going to Replace Static Languages?, Uncle Bob Martin asks whether test-driven development makes static type checking redundant. This is an interesting question, one I've heard pop up a lot the past couple years. This debate is often phrased in either-or terms, but I would like to steer the discussion more towards asking which kinds of programming language are a good fit for which kind of programming problems.
A Dual Personality
The two languages I currently use most in my day to day work are Java and Python. I use them for different kinds of tasks. Even though I too have experienced that I am much faster at coding things up in Python than in Java, when I need to build a larger industrial strength system, I still prefer Java. Even though Java's static type checking does slow me down in the ways dynamic typing proponents claim, I feel the compiler is helping me. On the other hand, when I'm doing what I call scripts, I use Python. In those kind of situations I feel Java's static type checking and other restrictions would be in my way.
Let me give another example of my dual personality. When I'm logged in as root on the Linux server that served up this very page to you, the rm command is aliased to rm -i. That means that any time I try and remove a file while logged in as root, the rm command asks me whether I really want to delete each file I listed on the command line.
Now, I don't remember the last time I said no to one of those rm command prompts. I try to be very careful when logged in as root, and so I almost always mean what I say. Let's say I only say no once out of every 10,000 remove commands on average. This extra verification step slows me down every time I try to do a remove, and it only catches one error every 10,000 checks. And it doesn't catch every kind of error. I could, for example, still say yes and only later realize I really didn't want to delete that file after all. Is all that slow down worth it?
For me personally, when I'm logged in as root I prefer having rm ask me "Are you sure?" each time I use it. The extra step slows me down, giving me another chance to think about what I really want to do. And when I'm logged in as root, I feel like it's helping me.
On the other hand, when I'm simply logged in as bv, I don't alias the rm command. I do a lot more removing as bv than root, and as bv I have less power to remove really important files. So in that situation, I would feel the extra "Are you sure?" check would be in my way.
For me, the amount of checking I want, whether I'm writing software or removing files, depends on the situation.
More Than Checking
The other thing that has been bothering me about the static/dynamic debate is all the focus on static compiler's catching of type errors as the reason for static type checking. I honestly don't think that the catching of static type errors is the main thing that attracts me to static languages for building systems. I think it is the formalism.
The languages we use, both human and programming, influence how we think. I like to think about building systems by partitioning them into APIs, and then designing the types and methods of those APIs in terms of contracts. And for me, I want to specify parameter types as part of the contract. In addition, I want to specify other restrictions on the input, such as a passed integer must be greater than or equal to zero. When I implement a method, I want to check to make sure the pre-conditions were met. I suspect I'll want to do that kind of thing when I'm building a system, no matter what language I'm using. It is how I think.
I try to be open minded. I see around me a lot of people whose opinions I respect excited about the productivity they feel building systems in a dynamically typed language. I want eventually to try building something more system-like, and less script-like, in a dynamic language, to see for myself. In the meantime, I just can't figure out what I would do about my cherished notion of designing systems in terms of designing contracts when using a dynamic language.
There's a whole installment of my interview with Guido van Rossum dealing with this question, Contracts in Python. Even after talking to Guido about it, I still don't think I quite get it, though I suspect what would be required is not that I figure out how to do contract-like design in Python. I suspect I would need to adopt a different way of thinking about building systems that fits the tool I'm using. Is it possible that this new way of thinking is what people find so freeing about building systems in a dynamic language?
Perhaps when I actually try building a system in a dynamic language I'll see the light. But perhaps I won't. I have observed that different programmers have different development personalities. It may just be that I am a contracts static type checking kind of guy.
Where Do You Draw the Line?
So here's my question: How many of you out there, like me, prefer to use both a static and dynamic language in your day to day work? For what kinds of programming tasks do you tend to use each kind of language, and why?
I was using Smalltalk long before I knew about TDD, XP, Agile, etc. I just prefer the freedom a dynamic language provides. Yes, you had to be more "careful", but it was worth it. Now, working test-first, I invest that care in writing tests, where it's captured and reusable.
I don't see a tight bound between TDD & dynamic typing. TDD makes it easier (maybe "safer") to use a dynamic language, but you gain almost as much from it in a static language.
I spent over 6 years sing mainly Smalltalk, and the last five years using mostly Java. My choice.. Smalltalk anyday.. any project... unless there's a very compelling reason to use Java. Part of that has to do with it begin dynamically typed, but there's also the exploratory environment... being able to type & execute code pretty much anywhere... working in a "live" system has such a qualitatively different feel to it that working with a collection of dead files.
> I spent over 6 years sing mainly Smalltalk, and the last > five years using mostly Java. My choice.. Smalltalk > anyday.. any project... unless there's a very compelling > reason to use Java. Part of that has to do with it begin > dynamically typed, but there's also the exploratory > environment... being able to type & execute code pretty > much anywhere... working in a "live" system has such a > qualitatively different feel to it that working with a > collection of dead files. > I really like using Python, but it isn't just the dynamic typing that appeals to me. I like trying things out quickly in the Python interpreter. I like the economy of finger typing. I like the readability of the code. I like the way the APIs are somehow designed such that using them also usually requires minimal coding. I like indentation-based blocks. I really like slices. All that stuff's really nice, and I think Java, were it to be designed over again, could benefit from some of these human factors qualities of Python. But this imaginary redesigned Java wouldn't have to abandon its static typing to gain those good qualities.
I guess I'm saying that for me at least, what I like about dynamic languages -- at least Python -- is many other things besides dynamic typing. What I like about static typing for building systems is that's how I do contracts. When I'm doing scripts, I don't care about contracts and I use Python. When I do systems, I do care about contracts and use Java.
When I'm doing API design, I try hard to specify contracts thoroughly and clearly, for example:
I talk a lot about what must be passed into the methods, and what the methods mean, the semantics. I try to express the contract at an appropriate level of abstraction, and give clues as to what subtypes are allowed to do. I can do all this with Python methods too, but several issues arise, all of which I asked Guido in this part of his interview:
I really need to build something more system-like in a dynamically typed language to see for myself, but if anyone can help me understand better what happens to the notion of contracts when building systems in a dynamically typed language, please post. What Guido's answers in the interview seemed to imply to me is that I may need to let go of attachment to this static typing contract notion, and kind of go with the flow. And lo, I will discover that I don't really need the static contracts after all. Either that, or there's a way to do the contracts in the dynamically typed language that I haven't imagined yet.
> So here's my question: How many of you out there, like me, > prefer to use both a static and dynamic language in your > day to day work? For what kinds of programming tasks do > you tend to use each kind of language, and why?
I really don't have a final opinion no this, but I would like to crontibute with experience that I had very recently:
I had to do a server where security was the most important issue and it had to be exposed via SOAP. I had an awful constraint: implementation language - perl.
With perl, when you have a subroutine you cannot even specify *how* many parameters that subroutine has.
I ended up having to check the number of parameters and I had to explicitly check their type. So I ended up writing MORE lines of code than if I used a language with explicit type declaration.
I suppose that WSDL would be a solution in this specific case, as it probably implies giving typing information and the perl SOAP server can be WSDL driven (but I never used WSDL, so take this rumble with a grain of salt)
I think at the end of the day I would like to have optional explicit type declaration: If I think my code *needs* type declaration then I should be able to do that, I think Strongtalk offers this (but never used it).
Another thing: And the fact that you don't have private members of a class... it means that if you want to expose an object via soap you have to put all your private parts in another class, so when you design you have to take into account the non existence of visibility scoping mechanisms. I think WSDL could also be of help here in the case of the perl specific implementation...
> I had to do a server where security was the most important > issue and it had to be exposed via SOAP. I had an awful > constraint: implementation language - perl. > Security was one other problem domain that had occurred to me for which I was also not sure how I'd approach without static type checking, but I didn't mention that because I really haven't thought or read much about it. So maybe there are ways to do security with dynamically typed languages that I just haven't been able to imagine.
Here's a simple example of how static typing contributes to security in Java. String is final, so you can't subclass String and override its methods and make it mutable. The security attack that prevents is that a String that is used in a security check, i.e., a security check for reading a file "fred.txt" is approved, can't be mutated into "bob.txt" before the String is used to open the file.
Does anyone have any experience with security in dynamically typed languages?
> With perl, when you have a subroutine you cannot even > specify *how* many parameters that subroutine has. > > I ended up having to check the number of parameters and I > had to explicitly check their type. So I ended up writing > MORE lines of code than if I used a language with explicit > type declaration. > Yeah, finding myself writing type checks at the input of methods in a dynamically typed language sounds like a warning bell that I'm not using the best fit tool for the job. Dynamic languages are a good fit in situations where you feel you *don't* need to check types at the beginning of methods. I'm curious if it is common practice to do some kind of type checking at some places within dynamic language systems. I.e., is there some percentation of method calls in the Python API -- 5%, 10% -- in which types are checked with specific checks at the beginning of methods?
I wonder how Objective-C fits into the picture. It appears to combine dynamic typing with static typing. You can choose whether you wish to refer to an object as id, which is like Java's Object from what I gather, or you can "typecast" the variable names to achieve static typing. The difference is that you don't have to narrowcast an object from id in order to call one of its methods (whereas Java requires the cast if the base class doesn't declare the method), thus an id-cast object is essentially dynamically typed.
I'm just learning Objective-C now, so I don't really have any experiences to contribute, but the language enjoys a great deal of support in the MacOS X community (including much of the software released by Apple). I was curious if anyone had any thoughts on this.
> Here's a simple example of how static typing contributes > to security in Java. String is final, so you can't > subclass String and override its methods and make it > mutable. The security attack that prevents is that a > String that is used in a security check, i.e., a security > check for reading a file "fred.txt" is approved, can't be > mutated into "bob.txt" before the String is used to open > the file.
This isn't a very compelling example though, because strings are immutable in most dynamically typed languages, such as Python and Ruby, too.
> Yeah, finding myself writing type checks at the input of > methods in a dynamically typed language sounds like a > warning bell that I'm not using the best fit tool for the > job. Dynamic languages are a good fit in situations where > you feel you *don't* need to check types at the beginning > of methods. I'm curious if it is common practice to do > some kind of type checking at some places within dynamic > language systems. I.e., is there some percentation of > method calls in the Python API -- 5%, 10% -- in which > types are checked with specific checks at the beginning of > methods?
I think in a lot of cases, a method simply uses a parameter in the way that it should behave; an exception will result if it is the wrong type (like if you try to iterate over an int). If it is a sequence, then it doesn't really matter whether it is a list or a tuple, in most cases (and in the cases where it does, an exception results, once again). I think with dynamic languages, you are kind of implicitly thinking in interfaces, rather than implementations, to the extent that you write a method that expects an object that behaves in a certain way (or has a particular interface, as it were); whether that object is-a Cat or is-a Dog is not important, as long as it can quack().
> I really like using Python, but it isn't just the dynamic > typing that appeals to me. I like trying things out > quickly in the Python interpreter. I like the economy of > finger typing. I like the readability of the code. I like > the way the APIs are somehow designed such that using them > also usually requires minimal coding.
These are generally features of a dynamically-typed language... and IMHO are results of it being dynamically typed. Have you ever worked in Smalltalk? If not you should (again as Martin Fowler recomended about trying TDD.. do it with someone good at it to start... to really see what you can do). An enviroment that supports those approaches makes them much more useable/effective/enjoyable.
> I guess I'm saying that for me at least, what I like about > dynamic languages -- at least Python -- is many other > things besides dynamic typing.
I would argue that the things you like are a by product of the lack of strict typeing... if not directly, then indirectly because of the attitude to programming inherent in them.
> I really need to build something more system-like in a > dynamically typed language to see for myself, but if > anyone can help me understand better what happens to the > notion of contracts when building systems in a dynamically > typed language, please post.
Test-driven development. The suite of tests are the contract(s). Furthermore they enforce the contract(s). Each test is like a clause.
> What Guido's answers in the > interview seemed to imply to me is that I may need to let > go of attachment to this static typing contract notion, > and kind of go with the flow. And lo, I will discover that > I don't really need the static contracts after all.
OK... I won't imply... that's exactly what you have to do.. Suspend disbelief long enough to try it out.
> Either > that, or there's a way to do the contracts in the > dynamically typed language that I haven't imagined yet.
Just take the red pill.
(yes, I am gearing up for the Matrix sequel next week :-)
I think that the unintended essence of Guido's argument is that Java, etc. go too far in increasing finger typing (and obscuring readability) without going far enough to actually help create correct programs. IMHO, the last sentence of your following paragraph points to the crux of the matter.
> The other thing that has been bothering me about the > static/dynamic debate is all the focus on static > compiler's catching of type errors as the reason for > static type checking. I honestly don't think that the > catching of static type errors is the main thing that > attracts me to static languages for building systems. I > think it is the formalism.
What is really important is how a language supports the methodology you're using. If the methodology is built on static formalism a language that supports the formalism will help make the methodology more productive, not the other way around. I first realized this when, after a period of trying to interest a development group in Eiffel, another engineer came in to my office and said: "You're really trying to convince us to switch to using the Design by Contract methodology, aren't you?" He was right. Switching languages alone would have been of no benefit. Switching to Design by Contract while still using C++ wouldn't have been much better because there is no clear, readable, easy way to use the methodology in that language. If it isn't easy to use a methodology, if the language and tools don't support, people will cheat. They will do just the minimum to satisfy the requirements because they don't have time to do it right.
While I lost that campaign, I still believe that Design by Contract is something thoughtful proponents of the benefits of static typing should look at. Read "Object Oriented Software Construction" by Bertrand Meyer for a (very) full exposition of this viewpoint. IMHO, Guido is right that simply declaring the types of variables increases finger typing without improving the overall quality of programs enough to justify the cost. Simple C/C++/Java assertions don't add enough more, even though they are a valuable tool. I believe that the full set of preconditions, postconditions, class invariants, and a development environment that supports them results in a completely different development experience. Maybe I'll have a chance to work with such a system some day.
Personally, I prefer the dynamically typed lanaguages. These days I'm quite fond of Python and have primarily used it alone and with C++ for about 5 years. Back in the 80s I worked in Lisp and still have a soft spot for it in my heart. But I wonder if the old statement about Lisp applies to dynamically typed languages in general:
"Lisp is a programmer amplifier. It makes good programmers much better and bad programmers much worse."
I think that case with Lisp is much more extreme that with Python. You have to work much harder in Python to create horridly obscure code and it's not so simple to write two simple expressions that do the same thing with an order of magnitude speed difference. But still, the main reason I was so interested in Eiffel was that I was not convinced that the company I was working at would have only good programmers...
> I think that the unintended essence of Guido's argument is > that Java, etc. go too far in increasing finger typing > (and obscuring readability)
Really? I agree about the increased finger typing, but I actually like what strong/static typing does for code readability. You can see right there exactly what type of object is pointed to by a reference without having to hunt down the reference's construction. You could make the point that a dynamically typed language lets you do the same thing by using a simple comment. But that doesn't strike me as any more readable than the type reference in the variable's declaration.
I'm more comfortable with staticly-checked languages (C++, Java, Objective-C) because I've used them more and they have certain helpful tools... If I ever get the chance to learn a Smalltalk environment by pairing with a proficient Smalltaker, I will, because I'd like to see Smalltalk-style Test-Driven Development and the Refactoring Browser in operation. Until then, being a newby at Smalltalk (the IDE and class libraries, I know the language) is too irritating for hobby-level programming.
Since C++, Objective-C and Java lack the refactoring browser, we can use the compiler as a refactoring tool: by changing the name of a method, and recompiling, we can find callers of that method (requires additional searching if inheritance is involved). If we add or subtract arguments of a method or constructor, or change a argument or return type, the compiler will tell us which callers need to be changed (MOSTLY: try changing an "int" to a "bool" or vice-versa in C++, and see how everything still compiles without errors.)
I'm used to such ways of refactoring (and you probably are too), so it's hard to imagine not having the compiler's support for those kinds of changes... Python, if I recall correctly, would not give you errors until run-time if you tried these same refactorings in that environment. Ditto for Smalltalk, but Smalltalk has the Refactoring Browser to make those changes safe.
I do agree with Martin and others -- you can't really say your code is correct unless you have tests, and if you develop test-first you always have tests, and those tests can make static type checking mostly unnecessary. And I'm really tired of type-casting and all the extra typing required by C++ and Java.
> I do agree with Martin and others -- you can't really say > your code is correct unless you have tests, and if you > develop test-first you always have tests, and those tests > can make static type checking mostly unnecessary. And I'm > really tired of type-casting and all the extra typing > required by C++ and Java.
Typing and testing are largely complementary. Even if you do have tests it is not credible to claim that your code is correct. Tests establish that a particular set of cases are not wrong but this falls far short of establishing that the code is actually correct in all cases. Typing guarantees that there are no "type" errors in the program but what this guarantee is worth depends to a large extent on the quality of the type system.
For example I had a discussion with a Smalltalk programmer about typing one day. He firmly stated that he didn't think static typing was worth the pain. However, after I pushed him a little he backed down saying that, in his opinion, the static typing in C++ wasn't worth the pain compared to Smalltalk. I find his original position to be indefensible while I completely agree with his amended position --neither C++ nor Java have particularly good type systems.
In my view typing is concerned with guaranteeing properties required for your program to work. These problems can be expressed as consistency constraints and are amenable to automated checking. In contrast testing is necessary to establish with some certainty that your program works as it should and this is a much, much harder problem. Testing also catches cases where the code fails to work (and in this sense it overlaps with typing) but this is mostly incidental as it is not being explicitly tested for and provides a much weaker guarantee than a good type system could.
> For example I had a discussion with a Smalltalk programmer > about typing one day. He firmly stated that he didn't > think static typing was worth the pain. However, after I > pushed him a little he backed down saying that, in his > opinion, the static typing in C++ wasn't worth the pain > compared to Smalltalk. I find his original position to be > indefensible while I completely agree with his amended > position --neither C++ nor Java have particularly good > type systems. I agree with this point. In my language exploration I have come to the conclusion - either do your static typing correctly or don't bother.
For an analogy, we have a security check as you come into our building. The security guards make you stop to have your bags checked. But when they "check" the bags they barely even look into them.
To me this is what the Java and C++ type systems are like. They are just enough to slow you down but really don't add any protection.
On the other hand, with a language like Eiffel with a well thought out and well enforced type system and the added features of Design By Contract I think you can add to both the clarity of the code and the safety.
I think that in many ways Design By Contract and Test Driven Development are what offer the same general benefits. If all you have is a type system like Java or C++ you are much better off with a dynamically typed language and TDD.
> I think that in many ways Design By Contract and Test > Driven Development are what offer the same general > benefits. If all you have is a type system like Java or > C++ you are much better off with a dynamically typed > language and TDD.
With all due respect, I think there are other reasons besides static typing to use Java or C++.
Uh, Objective C is dynamically typed. The compiler does some static type checking if you type your variable reference (using typed pointers) but it only issues warnings - never compiler errors.
Objective C has much more in common with Smalltalk than with Java and its ilk.
Flat View: This topic has 19 replies
on 2 pages
[
12
|
»
]