
Why I don’t like Dynamic Typing - jmount
http://www.win-vector.com/blog/2012/02/why-i-dont-like-dynamic-typing/
======
postfuturist
I 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.

~~~
Darmani
I spent much of a week debugging an issue in a Clojure multiplayer game where
the same action was being handled differently on different machines. Somehow,
the "true" branch of an if-statement was being taken even though the condition
was "false."

The reason? When the action was sent over the network, the boolean was
converted to a Boolean.

On the same project, I would also frequently suffer from name-typoes -- in
structure keys (deftype and defrecord didn't exist yet).

A dynamically-typed language is still dynamically typed. A lot of basic
analyses are still undecidable. Lexical scoping doesn't fix that.

~~~
gruseom
_When the action was sent over the network, the boolean was converted to a
Boolean._

That sounds more like an impedance mismatch between Clojure and its underlying
platform.

~~~
Ralith
And yet strong typing would have identified the issue immediately.

~~~
gruseom
How do you know that? Programs in statically typed languages can still have
bugs reconstituting objects serialized over a network.

~~~
Darmani
In a statically-typed language, I couldn't have used a Boolean as the
condition of an if-statement.

~~~
jmount
I like the point. And not being able to use if(0) (as in C) has caught tons of
bugs (like writing "if(x=0)" instead of "if(x==0)"). There are ton of things
that sounded convenient in the early history of programming languages that are
really anti-patterns.

For example:

    
    
      Confusing ints with booleans.
      Confusing ints with pointers (no longer done, thank goodness).
      allowing assignments to have a non-void type ("x=1.0" should be of type void, not type double).
      Poor choice of assignment and test equals (would be nice not to use "=" but to force ":=" and "==").
      Pverly aggressive type coercion and autoboxing.
      Integer division using the same symbol as floating point (many end-users want 5/2 to be 2.5 as the answer, mostly you want it to be 3 if you are doing loop-foo).
      Making "else if" and if-braces mere convention.
      Allowing null way too many places.
      Automatic variable decleration.

------
scottw
The 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
It has been my experience that when working on a large team using a dynamic
language that these issues actually appear quite frequently.

~~~
spooneybarger
It 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.

------
pavelludiq
I'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
Furthermore, 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.

------
16s
Very 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.

------
adrusi
I 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.

------
mistercow
There 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
> _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
> 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
Python 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.

------
carsongross
Static 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
Many 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
Komodo 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
In 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
You'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>).

------
lucisferre
Static 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

~~~
bunderbunder
For the sake of context, the original source of that quote is essentially an
advertisement for Squeak. It raises some interesting ideas, and Smalltalk is
definitely a system to know.

[http://squab.no-
ip.com/collab/uploads/61/IsSoftwareEngineeri...](http://squab.no-
ip.com/collab/uploads/61/IsSoftwareEngineeringAnOxymoron.pdf)

But as an argument for one paradigm vs. another it doesn't really stand up,
because the essay takes its own conclusion (that large systems would be easier
to maintain if they were more like Squeak) as a major premise. Him being one
of the inventors of the platform, I don't think we can just take his word for
it. At a minimum, what we'd really want to see is a large successful
enterprise system built on Smalltalk to serve as an instructive example. To my
knowledge no such system exists, so we can't really take the paper, insightful
as it is, as much more than hopeful musing.

That said, the "bind really late" approach has seen a lot of success. Just not
quite so pervasively as the "in all aspects" that Kay advocates in the paper.
Nowadays, the standard way to build large systems is to build completely
independent modules and couple them on fluid interfaces. Text streams in Unix,
REST APIs, and even SOAP are clear examples. What we don't see, though, is a
whole lot of reason to think that the languages and run-times on which these
modules run must also support late binding and hot-swapping of code at the
micro scale, or that we're really suffering for lack of it.

~~~
andrewl
_...what we'd really want to see is a large successful enterprise system built
on Smalltalk to serve as an instructive example. To my knowledge no such
system exists..._

Does such a system exist to _anybody's_ knowledge? I've heard that there's a
good bit of Smalltalk on Wall Street, but companies don't like to advertise it
because it's a competitive advantage.

~~~
igouy
“We have estimated that if we had built Kapital in another language such as
Java, we would require at least three times the amount of resources.”

<http://www.google.com/search?q=kapital+smalltalk>

------
akkartik
That _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
Although 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
Ah, 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>).

------
gcv
The 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> 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
> 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-
refactor...](http://beust.com/weblog/2006/10/01/dynamic-language-refactoring-
ide-pick-one/)

~~~
city41
At 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.

~~~
eta_carinae
> 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.

Like I said in my comment above, this has nothing to do with IDEA: dynamically
typed languages are just technically impossible to refactor automatically
without the developer's supervision.

~~~
city41
That's my point though. I'm not ragging on IDEA. I'm just using it as an
example because I see the reality of dynamic versus static languages every
day.

However, I _do_ feel though that IDEA made some poor choices and tried to get
refactoring and autocomplete capabilities in JavaScript when they probably
should have backed off, and IntelliJ's performance suffers (sometimes greatly)
because of it. As a simple example: if you use ExtJS and have it loaded in
your IntelliJ solution, try to rename a local variable named ownerct. IntelliJ
will completely lock up for about 15 minutes. Why? Because ExtJs uses the
variable "ownerct" throughout, and IntelliJ is mindlessly sucking in all those
references in. Of course you can set up your IntelliJ to avoid this situation,
it's just an example.

~~~
angryjim
If dynamic refactoring so easy they were doing it 30 years ago, but one of the
best IDE makers in the world can't generalize it for the masses today, doesn't
that tell you something?

------
learc83
I 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
<https://bitbucket.org/kotarak/clojurecheck>

(I recommend learning both languages tho)

------
losvedir
Am 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.

------
deepGem
Very 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.

~~~
gtani
One of my friends cranked a Mercedes E class up to 250 KPH outside of
Stuttgart. It was f###ing terrifying.

~~~
bwarp
My father tells me of such stories of the Autobahn. Usually terminated with
much pant-shitting by three Trabants trying to overtake each other...

------
dubya
Really good type inference like Haskell has is my preferred option.

------
finnw
I 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.

~~~
Peaker
His first buggy example would not be caught by a typical unit test...

~~~
finnw
The 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.)

------
gruseom
The 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> 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
The 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.

------
jmount
Thanks 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.

------
foreverbanned
I 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
Autocompletion 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.

------
einhverfr
I 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/

------
btbuilder
I 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.

------
slowpoke
His 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
The 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).

------
illumen
The 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!

