I don't think dynamic typing has any benefit.

I have tried the usual dynamically-typed languages, in particular Python, Javascript, Ruby and Lua, and so far have only found dynamic typing a hindrance, especially if a project grows to any adequate size. It may be non-idiomatic, but I usually end up writing a make-shift runtime type system for each function manually, i.e. type-check all parameters, make sure values are in the correct range, make sure return value is correct, etc. Most of the time, not having type checking doesn't introduce bugs, per se, since the usual problems that I will encounter will be obvious, but they are damn annoying. Some of the most irritating of these (taken from my experience with Python) are:

  • Forgetting to add '()' after a function name, so I assign the function to a variable instead of the result of running it
  • Forgetting to actually return a value from a function (which results in that function always returning 'None' which isn't great if that was intended to be a valid return value for that function)
  • Forgetting that I actually used a name for another variable of a different type, which gets overwritten
  • Overwriting a built-in function (I mean, great, that's really powerful, but I don't see the benefit)

As a consequence, I will generally only use such languages for small scripts, programming challenges, hacks, and just generally small or simple programs that I don't intend to maintain. Change my view, and let me know why we need dynamic types, and why Python/Javascript/Ruby/Lua/etc. made the right choice with using this system.

Edit: I'm adding another irritation I've remembered after reading Jon Skeet's post (link in comments), which is:

  • Forgetting what functions actually do (this applies somewhat to variables as well)

In statically typed languages, you can usually learn a lot from what types the parameters of a method are and what its return type is - knowing what I'll be getting back can usually tell me a lot, for instance, maybe the readlines() method which I'm expecting to return []string actually returns string. I'll actually have to check the documentation to confirm behaviour with a dynamically typed language, but if I make an incorrect assumption in a statically typed language the compiler will save me, and even tell me what type I'm getting if I have access to neither the documentation nor the source code of the method.

Having said all this, I also remembered one area where dynamic typing works astonishingly well in my opinion, and no amount of static typing or type inference will benefit, and that is:

  • Reflection

Reflection, in the few times that I've tried using it, is one of the few times when I'm actually battling against the type-checker to get things to work. It's actually a pleasure in Javascript to be able to trot all the way through the variables and methods implemented by a prototype, find the one you want, and get the result you want simply by giving the name of the variable/method you want at runtime. That said, I feel that reflection is such an unsightly, unmaintainable mess that the more deterrent I have from using it the better. Why do I feel this way about reflection? I feel that it's again because of the lack of types; you basically have to type-check everything that you're using at runtime, undoing all the hard work that the type-checker performed at compile time to make sure that the objects you'll be using behave.

Edit: Another benefit of static types is that you can often learn a huge amount about function simply by inspecting the types of the parameters and return values; Haskell has a resource, Hoogle, which allows you to find a function by name, but also by its type signature. Despite thinking that it was a niche, proof-of-concept feature when I heard of it, I have actually used it to great effect.

Being able to see the types of a function is very nice in documentation too, where you don't have to read the description of a function to find out what type of parameters it takes and values it returns, like you do with languages like Python (some languages get over the documentation issue by describing the types in function comments, like Ruby and some PHP codebases, such as Wordpress).

Posted May 11, 2013, 7:52 PM UTC · edited

1145
wvenable•

I've written large complex 10,000-file sized programs in dynamically typed languages and I agree with you. Static type prevents entire classes of bugs, makes refactoring easier, makes reading and understanding code easier, etc.

But some languages have incredibly weak and verbose static type systems that are frustrating to work with. But if you are working with a statically typed language with great generics and type inference, then it's actually very comfortable.

I'd like to see more languages have optional dynamic typing or optional static typing. Then you could choose how explicit you want to be and use the same language for small scripts and large projects.

6
tailcalled•

In a sense, many statically typed languages essentially have optional dynamic typing:

 data Dynamic = Str String | Dict (Map Dynamic Dynamic) | etc...
6
eZanmotoOP•

Very true; in OOP, if you just specify that every parameter type and return type is Object, you effectively have a dynamically typed system.

1
nullabillity•

Except for all the extra boilerplate casting. The only language I've seen so far with true boilerplate-less optional dynamic typing is C#.

4
julesjacobs•

Even in C# it breaks down once you get to higher order functions. The state of the art is typed racket & contracts.

2
wvenable•

No, you don't. Because you have to cast that object back to something to use it. In a dynamically typed language, you can call methods on untyped Object instances without having to give them a type first.

2
eZanmotoOP•

True, I forgot about that; I believe this argument applies to the parent comment as well in that case, except instead of casting you have pattern matching?

2
eZanmotoOP•

The idea of optional dynamic/static typing is a nice idea, but like any freedoms I can imagine it would get heavily abused by people who don't really know what they're doing or just generally lack discipline. I think it would also introduce an unnecessary verbosity, even for what was intended to be a small script (unless dynamic typing was the default).

3
Fabien4•

but like any freedoms I can imagine it would get heavily abused by people who don't really know what they're doing or just generally lack discipline.

That's not an argument. Good programmers will make good code, and bad programmers will make bad code, regardless of the language.

That said, if it's easy to make bad code, it's actually a good thing: It allows you to detect bad programmers, and get rid of them, faster.

3
eZanmotoOP•

True, but if it's easy to write bad code in a language, I believe that quite often the language will get blamed; I find C a lovely language to write in sometimes, but it is far too easy to shoot yourself in the foot with it and that's the part critics hone in on, despite how many unit tests and good practices you impose on your own code base.

1
jvictor118•

I find dynamic typing to be a time-saver for small codebases (sometimes even subcomponents of larger codebases) but shouldn't be the core language for a large monolithic software component. For example, I think it's great for 100-line "glue code" Python scripts to bind XYZ to a web service (or some stupid thing) but it's not good for the core infrastructure of a large mothatfuckin ABC

4
kqr•

Why would you want to defer type errors to run-time in small code bases but not in larger ones? What do you benefit from deferred type errors?

1
jvictor118•

Because debugging is fundamentally easy in small code bases so that doesn't matter as much. So the benefit of quick n dirty outweights the debugging issue.

Consider a 20 line script that shows an iterative solution to an optimization problem. Its basically a demo. Debugging it isnt hard -- what could have possibly gone wrong? A typo, a missing restriction... all easy stuff to find by running it, seeing what went wrong, etc. Also i find in such little ditties you're often demoing something where most of the bugs that could exist would happen at runtime anyway. Just experience talking though, no real rules to back this up

EDIT: put another way, I find it useful for pieces of code so small that it doesn't really need a type system to guide its structure

2
kqr•

What I don't get is that given a "correct" piece of code, the static and dynamic type systems behave exactly the same. Their differences only show through in "incorrect" code -- in which case the difference is that the static type system will tell you wat yo did wrong up-front, while the dynamic type system will sort of let you discover that by yourself. I don't see ow less automation would be useful for any sixe script, although it's less of a problem with smaller scripts.

1
jvictor118•

You're not taking into account the gains you get from less time spent on the code. Most times I write a python script that's a main goal. It's to prove something, or bind something very simple together, or whatever. It's not a good language to build large architectures in, although I've seen it done.

Also, like I said, in the use cases where dynamic saves you time, often the main things you'd be worrying about would be fundamentally runtime, i.e. your search algorithm causes a stack overflow. No typechecking helps with that.

When you have a program so small that the typesystem is basically nonexistent and you just use collections and numbers, what is the benefit gained from typing a bunch of boilerplate code in a static language?

1
LHCGreg•

The venerable Jon Skeet tackles the same question in this blog post. Although people give examples of cases where dynamic typing would be useful, he is not convinced that there is benefit to making an entire language dynamically typed.

3
BonSequitur•

One note about Ruby - Ruby's not quite as dynamically typed as Python, and the examples you give can't happen in Ruby. In Ruby...

myvar = myfunc()
myvar = myfunc # Completely identical.

In Ruby all functions return something. It might be nil, but the last statement of a function (All Ruby statements are expressions) is also its return value. This means something like:

def myfunc
  if true
    "Truthy!"
  end
end

Returns "Truthy!" as a string. Ruby will not type coerce variables (Except in the case of arithmetic, which will convert numbers between int/float as necessary). So mystring + mynumber will fail. In most ways that count, things are actually statically typed; most objects just implement to_s, to_i methods and so on which are only rarely invoked implicitly (If I recall correctly, only during string interpolation). So Ruby is far from being as dynamically typed as, say, JavaScript.

On the other end of the spectrum, we have Perl, which I believe is a language with loose typing done right. Perl has scalars, arrays, and hashes, and the three don't really mix - though functions expecting an array will treat a scalar as a one-item array, and functions expecting a hash will treat an even-numbered array as a hash. It has three types of scalar values - numbers, strings, and references. Though references have to be kept separate from other values, it's usually extremely obvious when a scalar reference is being used as a different kind of value (References to scalar values are very rare in Perl). As for numbers and strings, each has their own set of operators; functions and operators define context, not the variables themselves, so it is immediately obvious how a variable is being treated. Problems 1 and 2 also don't exist in Perl.

The third problem you mentioned - clobbering variable names - sounds to me like a bad habit people form in statically typed languages. In fact, I'm a little at a loss as to how any reasonably written application in an OO language, can have variable name clobbering issues, unless it's written in a language with really primitive scoping rules... like JavaScript, which is awful, but not because of dynamic typing so much as the badwrong way in which JS implements dynamic typing.

2
tonnynerd•

The fact that Ruby doesn't coerce variables with different types implicitly has nothing to do with it being dynamic, but with it being a strongly typed language, as Python also is. It's another kind of difference, strongly versus weakly typed, and has, I think, nothing to do with the dynamic versus static typing one.

5
BonSequitur•

Yep. I was mostly addressing the OP's complaints about dynamic languages which, in retrospect, are more complaints with weak type systems.

Though I can't think of a single statically-typed language with ‘weak’ types in the sense of implicit coercion and polymorphism/duck typing. It seems hard to implement without dynamic types, or maybe it would invalidate the advantages of static typing (Because you'd have to defer some level of type checking until runtime).

1
iopq•

An example of a statically typed language with weak typing - C. Chars are just shorter ints and you can add and subtract them.

2
BonSequitur•

True, I never thought about it that way.

1
tonnynerd•

Indeed, C is what comes to mind. It's the only one, though.

1
iopq•

That's not static typing, that's strong typing. The types can be changed at runtime, which is why it's still dynamically typed.

1
julesjacobs•

I would tend to agree with the gist of what you say, but I think that dynamic checking has a lot of value once you go to advanced type systems like dependent types or refinement types. There you can use the type system to prove a lot of properties about your programs, but the downside is that you have to write those proofs explicitly. In many cases, that's not worth the trouble. But it would still be great if you could use the same types to specify your specification, and if a proof is missing the compiler would insert run time checks. Then later if you decide it's worth to prove the thing, you add the proof and the run time checks go away. For example if you have a function foo that only accepts prime numbers, if you pass it some number n that is not known to be prime, the compiler does this:

assert(isPrime(n));
foo(n);

If you later add a proof that n is always prime, then the compiler no longer needs to add the assert.

I would add though that in many cases I'd rather program in a language with no static type system than a weak type system like Java. That gets in the way more than it helps.

2
eZanmotoOP•

As mentioned in another thread, only Java primitive types are weakly-typed, as they are in many other languages; do you mean a weak type system in the sense that it is not implemented very well?

1
julesjacobs•

Ah yes I shouldn't have used the term weak because that already means something else. I meant weak in the general sense of having few good qualities. In particular the type system is not very expressive, for many things you need casts. The things you can express require an inordinate amount of boilerplate. The syntax for the types is very ugly and unreadable. The rules for dispatch are flawed (e.g. you cannot dispatch on a type parameter). Covariant arrays. Overcomplicated variance handling in general. Etc.

1
TimmT•

There is a research paper about this from Microsoft (link) that identifies some some use cases and how to work around them.

The thing to keep in mind is that static typing does come at a cost too. If your program does indeed need to exhibit dynamic behavior (e.g. multi-purpose servers), or is working with non-strict types to begin with (e.g. JavaScript operating on literals embedded in the html, generated by server side code), then static typing doesn't buy you much, other then a false sense of security .. but you're still paying the cost.

1
Fabien4•

Forgetting to add '()' after a function name

Every language has its beginner's gotchas. Long ago, when I was learning C, I remember being bitten more than once with this:

for (i=0; i<10; ++i);
  {
   f (i);
  }
1
eZanmotoOP•

I wouldn't regard it a beginner's gotcha, I find myself forgetting to actually add '()' after a function every so often, which will introduce a bug that I'll only find on inspection. This is not because I don't realize that a function can be a value, as I would as a beginner, but that I simply forgot to call that function. A type-checker would be immediately able to tell me that the following isn't allowed in the case that f is a function, reminding me to add parentheses:

int x = f;
2
Fabien4•

Forgetting what functions actually do

I completely agree with your paragraph.

There is, however, a case where you won't forget: if the program is short and/or short-lived enough.

Let's say I want to create eight files: blob2.txt, blob3.txt, ..., blob9.txt. I can write:

for ((i=2; i<=9; ++i))
do
   touch blob$i.txt
done

Would you say that this Bash program would have been better with static typing?

1
LHCGreg•

Would it have been worse?

4
kqr•

The same program with static typing:

forM_ [2..9] $ \i ->
  writeFile ("blob" ++ show i ++ ".txt") ""

I would argue it's better since I get some guarantees statically. (In fact, static typing "saved" me once. I accidentally tried to do + instead of ++ and it told me before I even got a chance to run the program.)

2
[deleted]•

[deleted]

1
eZanmotoOP•

One of the things I heavily dislike about Python is the fact that you can write functions like the second example, because I know that only a subset of types work with the + operator, but I'll have to wait until runtime to find out if some call to the function will fail, by getting a type exception thrown. I'm not sure how the C++ example would work, but I know that a Java/C# version of the same code won't compile.

2