10 ms·
Why I don’t like Dynamic Typing
- finnw 15y agoI disagree with the bit about test-driven development. It is not necessary to add tests specifically for type errors. Unit tests for basic functionality will usually catch them.
- bunderbunder 15y agoThey won't do so reliably if you're working in a larger or longer-lived project. Then there's much more chance for a mental disconnect between the writing of a procedure and the usage of the procedure. (Either in the form of each being done by a different person, or the same person but with some 'forgetting time' in between.) There's also much more chance for the source to start to acquire a large collection but almost-but-not-quite-the-same types. The unit tests that were written with the original procedure are likely to only cover usage scenarios that are expected by the person who just wrote the procedure. Someone who's not in that headspace can easily be a lot more "creative" about coming up with surprising ways to try and use the code. So easily that it might even happen by accident. Like, say, as a result of a simple refactor. So in a reasonably-sized project, unit tests for basic functionality end up being a rather short Maginot Line for type errors.
- Peaker 15y agoHis first buggy example would not be caught by a typical unit test...
- finnw 15y agoThe integer overflow bug? It depends on the quality of the test. It makes sense to test with the same types you intend to use in production, and to cover a wide range of values within each type (the array [1,2,3] for example would not be a good test for 64-bit integers.) And static typing would not necessarily catch this bug anyway. A C++ template or a polymorphic Haskell function would fail in exactly the same way. What is called for here is argument conversion (A C function or java method declared with double arguments would convert its arguments automatically, but no reason you cannot convert explicitly in a dynamic language.)
- einhverfr 15y agoI generally agree with the bit about restricting data to proper types and that this makes the code more readable and reliable. Although not quite the same thing I've been moving towards more rigorous typing through Moose and wrapper classes in my Perl development work/
- learc83 15y agoI learned to program with Ruby and used it pretty much exclusively for 2 years. However, after I learned Java and C++. I found that I really preferred static typing, and the error checking you get from the compiler. I've been learning Clojure, but I'm thinking of switching over to Haskell for my next project mainly because it has static typing (also because I'm intrigued by QuickCheck).
- gtani 15y agohttps://bitbucket.org/kotarak/clojurecheck https://bitbucket.org/kotarak/clojurecheck (I recommend learning both languages tho)
- dubya 15y agoReally good type inference like Haskell has is my preferred option.
- lucisferre 15y agoStatic typing has it's place, but it's been massively oversold. I'm interested in static languages that have excellent type inference or like dart with it's optional static types,however 90% of the time in strictly static languages like C# and Java it gets in the way more than it ever helps. Complaints like "refactoring" or compiler error checking however are the oldest FUD in the book. > Until real software engineering is developed, the next best practice is to develop with a dynamic system that has extreme late binding in all aspects. > --Alan Kay
- chi_h 15y agostatic typing does NOT mean C# and java - there are far better statically typed languages out there.
- lucisferre 15y agoAgreed. But they do embody the strict static typing philosophy which I do acknowledge is clearly a straw man of the authors point of view.
- bunderbunder 15y agoC# has supported dynamic typing since version 4.0. At this point it doesn't make much sense to say that C# is strictly anything. It's perhaps the most obsessively multi-paradigm language out there.
- lucisferre 15y agoUgh, `dynamic` doesn't provide the benefits of a dynamically typed language by long, long way, and yes I've used it extensively. But thanks for the downvote all the same. http://wekeroad.com/2010/08/09/csharps-new-clothes/ http://wekeroad.com/2010/08/09/csharps-new-clothes/ In case you plan on skimming that and missing the details the tl;dr is that Rob, pushes `dynamic` as far as he can and it still comes up far short of the extensibility and expressiveness of Ruby constructs.
- scottw 15y agoThe claim that "Debugging is one of the most expensive steps in software development- so you want incur less of it" is something we can all agree with, but "even if it is at the expense of more typing" doesn't follow. Having programmed in both kinds of languages, I have to admit I love not thinking about the type. I don't buy the claim that static typing saves much in the long run (in terms of debugging or writing better code). In 15ish years of using both static and dynamic typed languages, I think I've been burned two or three times at most by not having static types—it's just not a big problem. Using static types creates a self-fulfilling environment in which getting the right type is crucial (and thank goodness we have a modern IDE to help with that).
- keypusher 15y agoIt has been my experience that when working on a large team using a dynamic language that these issues actually appear quite frequently.
- spooneybarger 15y agoIt is my experience with large teams that just about every issue starts to appear frequently as the overall quality on large teams is harder to maintain than on small teams where coordination is easier and you can keep the skill level of the average developer higher.
- tikhonj 15y agoI think the issue is that there are good statically typed languages and there are bad statically typed languages but only the latter are popular. I've done a good bit of programming in Java and that was painful, with relatively little benefit. In fact, for a pretty long time, Java made my an adherent of dynamic typing. Then I tried Haskell and realized that a good static typing system is very useful. It not only catches mistakes--I've worked on very language interpreters in both Python and Haskell, and I constantly made preventable mistakes in Python--but actually makes your code more expressive. Take the read function, for example. (It is basically the opposite of toString.) I can't think of any way of writing it in a dynamically typed language because it has to know what type it's supposed to be to work--it's polymorphic on its return type. And this is very useful: if you have a string, you can just read it in and it will be of whatever type you need (as long as you've defined how its read). Haskell has some other advantages over Java (e.g. much more lightweight types and no messing about with classes and subclasses), but I think the important point is that static typing done right is not only safer but also sometimes more expressive than dynamic typing.
- 16s 15y agoVery good points. I agree 100%. However, I think you can and should mix the two. I write mission critical code in C++ with all compiler warnings enabled and lots of unit testing. I do less critical code in dynamic languages (such as Python) where I can 'import critical_cpp_module' and use it from within the dynamic environment. This approach allows for the best of both worlds. I get the benefit of quick development time, high-level Python libraries and the ability to be sloppy without being punished while at the same time I have strict, very strongly typed and tested C++ code for the things that just have to be right no matter what. When you combine the two approaches, you're more flexible. I suggest Boost Python for those who wish to expose C++ functions, classes, etc. to Python. Edit: Software such as pylint are nice too if you're going to go dynamic only. That helps catch lots of bugs that you'd not otherwise notice.
- deleted 15y ago[deleted]
- mistercow 15y agoThere are actually some really good points here. I was bracing myself for the typical anti-dynamic-typing article that merely confuses dynamic typing and weak typing, but no, these are good points. I will say, however, that weak typing can be used to overcome the first complaint. For example, in Python, if you were to do apply a function that resulted in integer overflow, you would get an automatic conversion to long integers. (The actual example doesn't really apply in this case, since to make that function work at all in Python, you'd have to to make `n` a float, which would fix the problem anyway). Edit: And yes, I know that Python is a strongly typed language, but implicit conversion to long integers is still an example of weak typing.
- mindslight 15y ago> implicit conversion to long integers is still an example of weak typing No, it's an example of a properly defined integer type. Letting "integers" silently overflow is weak typing. If you explicitly want wraparound arithmetic (by proving bounds for performance or wanting implicit modulo), then explicitly specify int16/int32/int64.
- mistercow 15y ago> Letting "integers" silently overflow is weak typing. "Weak typing" is a broad concept, but that's not one of the ideas that it covers. In any case, Python's conversion of integers to long integers is not transparent. It is an actual change to a different type, which is an example of weak typing.
- kstrauser 15y agoPython never converts an int to a long. Instead, it may return a long from the result of an operation on an int if that result wouldn't find in an int.
- quatrevingts 15y agoThe term "weak typing" does not really mean anything and confuses a lot of discussions about programming languages. Here you are clearly talking about implicit conversions, but weak typing is also used to refer to completely unrelated things like casts in C (which can be unsound). Python having both "strong" and "weak" typing illustrates this: these terms are ill-defined. Edit: mindslight's comment adds another possible interpretation of "weak typing" (integers which overflow), further illustrating my point.
- carsongross 15y agoStatic verification is great and all, but the most important reason for static typing is UI/UX: Hit-dot-see-what-I-can-do. No keeping APIs in my head or losing flow to a google search, hit dot, see what I can do. Everything beyond this is gravy.
- DanHulton 15y agoMany good editors/IDEs will do this for you fairly reliably. Komodo and Sublime Text both support this for PHP/JS at a minimum, as an example.
- WayneDB 15y agoKomodo relies on getting type info from JSDoc notation and Sublime Text barely does autocomplete at all. For instance type this into Sublime and you won't get a list of String methods when you type the last dot: var x = "Hi!"; x. I'm not sure how Komodo does with that one without any JSDoc notation. I'm know that there are editors that do a slightly better job (for instance, Visual Studio probably has the best Javascript autocomplete I've ever seen) but still, it really pales in comparison to how well it works with statically typed languages.
- Too 15y agoIn visual studio your example would work. You can even do it on user defined types. It's pretty accurate and actually finds every place where it seems reasonable where it could have the information. But it doesn't go much further than that. For example parameters inside functions don't have intellisense and return values from functions rarely do, but sometimes.
- WayneDB 15y agoYou're right about parameters inside of functions, but wrong about return values. It does work for return values as long as visual studio can deduce the type by running the code. For parameters, you have to help visual studio out by documenting what the parameter does using XML code comments (e.g. /// <param name="arg1" type="String">Arg1 description</param>).
- pavelludiq 15y agoI've fallen in love with hybrid systems like Common Lisp. SBCL is dynamically typed, but type inferred and I have the option of adding type declarations to help the compiler out. It's a different way of thinking, you're working on a living system, instead of treating your program as a monolithic block that the compiler has to prove satisfies some static constrain, you work on chunks(functions, classes, etc.) that you compile and test immediately while your program is running, and if you screw up, the system is there to help you figure out what went wrong and fix it, without killing your process. Wetter or not this type of incremental development is better or not for some definition of better, I feel it fits the way I think better. Maybe somebody with a different way of thinking will actually perform worse in such an environment, thats my theory at least. I know I'm faster(and much less annoyed at my compiler) this way.
- ced 15y agoFurthermore, SBCL type declarations are - Statically checked and removed at compile-time if possible - Checked at runtime otherwise - Not checked at all if you compile with high speed and low safety, yielding close-to-C level of performance (complete with memory faults and weird bugs when things go wrong) Furthermore, I can turn on this high speed/low safety mode in specific portions of my code (inner loops), while compiling the rest with high safety. SBCL + SLIME + Quicklisp is a seriously awesome combination.
- postfuturist 15y agoI recently learned enough Standard ML to be dangerous, and I gained enough of an appreciation of ML-style static / type inferred languages to see where the author is coming from. I even agree to some level. But, in practice, some dynamic languages just don't seem to suffer from hidden type bugs. One of those is Clojure. Instead of packing everything into complicated types, you are mostly working with maps and sequences of maps--just raw data--with higher order functions. There isn't a lot of room for type bugs. Additionally, without any input from the developer, Clojure does some type-inference all on its own. You can enable warn-on-reflection such that the Clojure compiler will tell you anywhere in the code where it's going to have to use reflection to figure out how to invoke a Java method or property on an object. You can fix them with type hints such that you get performance on par with statically typed Java. Clojure protects you from name typos you might see in other dynamic languages and accidentally clobbering existing variables through the fact that most variables are really not variable at all. You bind a value once in a lexical scope and it can't be mutated. That's more of a benefit of functional languages and lexically scoped languages, than statically typed ones. It may boil down to a matter of taste, but I don't see the need for fancy refactoring tools like "method extraction" in a language like Clojure. The methods already don't live in classes, so they hardly need to be extracted from them :). If you rename a method in Clojure, the compiler _will_ complain in all the places where you are trying to call a method that no longer exists.
- pron 15y agoYep. Same here - I had always thought I agreed with all the arguments mentioned in the post, and then I found Clojure. I'd like to add that I don't know if there are any existing refactoring tools for Clojure, but there certainly could be. All the things that you might want to rename safely - say functions and keywords (used for hashmap access, and serve as sort of Clojure class fields) are proper symbols, well defined in a namespace. You could have a tool that renames them safely. In Scala, OTOH, a statically typed language, you can't even rename methods safely because they're not always referenced through their defining types, as they could be called through an injected so called "structural type". So nowadays, some statically typed langiuage lose, through cleverness, many of the advantages of static typing, while dynamically typed languages, through disciplined restraint, manage to gain them. EDIT: actually, the one thing I miss most from statically typed languages is the immediate error highliting in the IDE. The REPL alleviates the need for that, but it was still cool immediately seeing files and lines highlighted in red as soon as I type in some error. I wish Clojure could do that.
- deepGem 15y agoVery good points. I was initially enamored by the glamor of dynamic typing and to some extent I still like the concept of not having to declare anything. It's like driving on the Autobahn without any speed limitations. However, since I hav en't done large scale software development using dynamic typing, I haven't faced bugs that are mentioned in this article. It takes a lot of discipline to reduce debugging costs in a dynamic typed language and I agree to the author's point that why deal with enforcing such discipline when you can simply use a statically typed language. +1 to the refactoring example.
- spooneybarger 15y agobecause you need discipline in any programming language regardless of your typing system? programming requires discipline. using the discipline required to do X as reason to not do X isn't going to get you very far.
- hackinthebochs 15y agoThis argument is silly. Some things require more discipline than others. Using tools that inherently enforce discipline is easier than enforcing it through self-discipline, convention and constant reviewing.
- deepGem 15y agoThank you. Exactly what I wanted to convey.
- gtani 15y agoOne of my friends cranked a Mercedes E class up to 250 KPH outside of Stuttgart. It was f###ing terrifying.
- bwarp 15y agoMy father tells me of such stories of the Autobahn. Usually terminated with much pant-shitting by three Trabants trying to overtake each other...
- adrusi 15y agoI learned to program in dynamic languages (that's not quite true, my first couple of weeks programming were in Pascal), primarily javascript but also some ruby and PHP. I continued using mostly javascript for about 2 years, and I was strongly opposed to static typing (largely because of my exposure to Pascal, but also because I did a whole lot of crazy stuff in javascript that was completely dependent upon weak dynamic typing). Then I learned Haskell, which I was more open to because I had heard I didn't need to write down any type information. With Haskell I learned that static typing was great because it created a documentation for your code that was self enforcing, and as Haskellers love saying, if the code typechecked, it probably works as expected. Now I prefer working in a statically typed environment. But for none of the reasons presented in this article, which I think is just presenting information that those used to dynamic typing will turn their head at.
- slowpoke 15y agoHis example for the second point is horrible and contrived (not to mention stringly typed, which is an anti-pattern in every language). If anything, the "unhandled" should be assigned in and "else" block, not by implicit "fall-through". That would (probably, I don't know R) cause a failure for an undefined variable when trying to return it - which is exactly the same as having the compile complain about an undeclared variable. In general, I just disagree. Strictly typed just isn't superior to dynamic typing at all. It's a different approach with its own pitfalls, and more than enough of them.
- jmount 15y agoThe final else block is a good point. In R this would cause an error during the attempt to return a non-existent variable (so at least R doesn't form a new variable on reference, just on assignment). This better is you now can see the error if it occurs. But it isn't everything as you may see this first in production (if you have insufficient testing). I still like the bug to not be at all possible. For example in Java we could declare the variable final at the top of the block and then the compiler would only accept code where the variable is assigned exactly once on all code paths (no matter how unlikely the path). This would catch a bunch of other popular errors (like forgetting to type "else" before one of the chained "if"s (as "else if" is unfortunately just a convention).
- gcv 15y agoThe author's second and third points are either disingenuousness or ignorance. * The omission of variable declarations has nothing to do with dynamic typing. Some dynamically-typed languages certainly do this (Ruby and Python). Some dynamically-typed languages make variable declarations optional or optionally-required (JavaScript and Perl). Some dynamically-typed languages require variable declarations (the Lisp family), and so do not suffer from the identifier misspelling problem. * In my experience, Eclipse and other IDEs are not completely reliable when it comes to refactoring and variable renaming. Yes, they are good at it, but at the same time, they all offer preview modes and encourage the user to double-check the IDE's changes. I have seen Eclipse fail in renaming identifiers in Java code. For Lisps, I have found http://brian.mastenbrook.net/display/26 http://brian.mastenbrook.net/display/26 to be just about as effective as Eclispe's Java renaming. As for the first point, the misuse of the word "macro" makes the entire argument rather difficult to address. It seems to boil down to taste.
- eta_carinae 15y ago> In my experience, Eclipse and other IDEs are not completely reliable when it comes to refactoring and variable renaming. Yes, they are good at it, but at the same time, they all offer preview modes and encourage the user to double-check the IDE's changes. I have seen Eclipse fail in renaming identifiers in Java code. True, but an IDE will refactor accurately much, much more often on a statically type language than a dynamically one. Refactoring dynamically typed languages is pretty much impossible to do automatically when you don't have type information: http://beust.com/weblog/2006/10/01/dynamic-language-refactoring-ide-pick-one/ http://beust.com/weblog/2006/10/01/dynamic-language-refactor...
- igouy 15y agoNow read comments #30 #31 and #32.
- city41 15y agoAt work we have a Java backend and a very heavy/complicated JavaScript frontend. The frontend was born more recently while the Java backend has been around for years. Due to this situation, IntelliJ is the company-wide recommended development environment and most often used. IntelliJ is largely considered to be a quality IDE. And when using it for Java, it is. When using it for JavaScript, it's not only borderline useless but often downright counterproductive. We also have quite a bit of tooling written in Ruby, and most also use IntelliJ for that as well. Again IntelliJ falls on its face with Ruby too. IntelliJ's ability to refactor Ruby and JavaScript is a joke and really just amounts to global searches and replaces, and wildly inaccurate results. I typically get out a plain text editor and ditch IntelliJ altogether.
- akkartik 15y agoThat variance example seems contrived. I'm expected to believe that the programmer explicitly asks for integers in a dynamically-typed language and then is surprised by integer overflow? "in a statically typed language the language would force.. coercion" But R just did an implicit coercion, right there. You gave it ints, it gave you back 1e12. I don't know R well enough to understand what the int() call does, but there's no reason why a dynamic language can't perform implicit coercion. Python does: >>> type(1000000) <type 'int'> [define sumXX] >>> sumXX([1000000,2000000,3000000,4000000,5000000]) 55000000000000L # <--- type long I could wrap the elements of the list in int() calls but that would change nothing. The whole point of dynamic languages is that you don't have to think about the types of your numbers. They're just numbers. If you have a use case that requires "4-byte integers, goddammit!" (and there are many valid ones), why yes, you should use a static language and think about overflow. If you don't want to think about overflow, the languages that go to the greatest lengths to shield you from it (lisps) are all dynamically typed.
- chc 15y agoAlthough many static languages do force you to choose a width for your integers, there is no inherent connection between static type systems and fixed-width integers. For instance, Haskell is one of the most strongly static languages out there and it has an unbounded Integer type. The connection between static typing and integer overflows is an artifact of older statically typed languages like C whose type systems were designed to model the hardware.
- akkartik 15y agoAh, good point. So the whole notion of unboxed integers seems increasingly orthogonal to dynamic typing. And now so do explicit variable declarations (http://news.ycombinator.com/item?id=3633520 http://news.ycombinator.com/item?id=3633520).
- illumen 15y agoThe three main points in the argument are wrong. I present as evidence this flower and a pack of cards. Dynamic languages DO have refactoring tools. It's just that not many people use them. That typo error WOULD have been caught with a static analyzer or with unit tests. Yes, if you supply a function with a type you haven't tested for it might not work. Solution: test for that type, and validate your inputs. The fourth argument that debugging is more expensive is not substantiated. I find dynamically typed systems easier to debug, especially when they don't have four hour compile times like some statically typed code bases do. Being able to more quickly change, and rerun code in dynamically typed systems gives them a big debugging advantage. Many dynamically typed systems even let you easily change code at run time (which I know is possible with statically typed systems too). I think more major reasons would be dependent on the types of problems people are solving. Also... vi forever!
- foreverbanned 15y agoI think autocompletion as a selling point for static language is underrated. With autocompletion I feel I type less than half the characters that my code requires. It is not just about completing a word. The completion can be a pattern of several lines of code with placeholders where the IDE jumps and waits for you to enter one or two characters that it completes again. The resulting experience is that things just flow. The IDE frees your mind from details like name spelling, api method list, exact language syntax and usual idioms. And it is probably just the beginning. The completion is still pretty basic when you think about it. At some point maybe, IDE will switch to a rule engine to manage thousands of completion rules.
- eropple 15y agoAutocompletion tools are getting pretty good as it is. IntelliJ for Java and Resharper for C# are unnervingly good about predicting exactly what I would have typed even for stuff like declaring method names.
- btbuilder 15y agoActiveState's Komodo has autocomplete for some dynamic languages like Python. Obviously its not possible to be as accurate as static type languages, but it helps a lot.
- gruseom 15y agoThe OP argues that although programs written in dynamic languages are smaller, they have more bugs. But empirical studies find the opposite: bug counts grow superlinearly with program length irrespective of language. In fact, program length is the best predictor of error rate we know. So his argument contradicts the evidence. Chapter 27 of Code Complete (2nd ed.) discusses this. So does http://news.ycombinator.com/item?id=3037293 http://news.ycombinator.com/item?id=3037293 among other HN threads. Also, if anyone has access to Capers Jones' "The Impact of Program Size" from Programming Productivity (1986) and is feeling generous, please cite the relevant findings. I can't find it online.
- jmount 15y agoThe empirical evidence is as you say. It just hasn't been my experience that code bases with the same number of tracked defects are all the same. Some are brittle and littered with rare lurking bugs (rare in the test environment, but guaranteed to be hit by many in production) and other code bases are much more stable. I think this depends on the style, the rigor of design and the discipline seen throughout development. So even if my experience with static typed code bases has been more pleasant, dome of that could be due to the type of developer that uses them.
- btbuilder 15y agoI work on a team of about 10 people with Python on a pretty complex project. We have not experienced any typing issues in deployed code. I think this can be attributed to a combination of strong unit tests and type enforcement when constructing objects that are eventually serialized to the database. Python is stronger than some other dynamic languages at type enforcement too. I believe I would prefer compile time type checking, but when working with a lot of dynamic data structures (say, JSON or XML) there is an ease that dynamic language toolkits can bring that a lot of the time evade more formal languages.
- losvedir 15y agoAm I the only one who thinks in types? Whether I'm coding Ruby, C, or Haskell, I'm always conscious of what type signature the function I'm writing has. "Okay, this function will take two arguments, one will be an integer, and one will be a string, and it will return a list of strings." It helps me reason about my code since I'm never going to want to, say, add an integer to a string, without some intentional type conversion in there. I'm diving into haskell at the moment and really liking it. I always notated to myself what types a function took and returned, even if it was just in a comment, but with haskell there's a notation for it, and the compiler is pretty sophisticated about making sure that type-wise my function is doing what I'm expecting it to be doing.
- jmount 15y agoThanks for the comments, I am learning some things. I have a longer comment in my article now- but I would like to point out the two bugs in question actually happened. The first one never made it into production because I deliberately write in small modular pieces and then use Emacs incremental search to search on important variables (causing all correct spellings to highlight so incorrect spellings become obvious in the deliberately regular format I write in) before checking in. The int problem did in fact happen. I didn't add a stupid int-cast (as in the example) but an R ODBC driver promoted a result to int based on something it secretly learned from the DB schema.