I was reading this question on Stack Exchange asking what is possible in a dynamically typed language that is impossible in a statically typed language. Most of the responses were terrible. It reminded me that the level of discussion surrounding the topic of types in general is very disappointing. Basically, most people do not know what they are talking about.
Luckily, one of the answerers posted a link to this post about debating type systems. I had never read it before and I am glad I have, now.
The author takes the approach of describing how people generally use typing terms as opposed to prescribing a definition. His main point is that the terms are generally misunderstood so much that trying to discuss them is not productive. There is no agreed-upon definition in most contexts. I agree. I wish it were not the case, though.
But there was also another gem in the article.
Dynamic and static type systems are two completely different things, whose goals happen to partially overlap.
I think this point is right on. Static type checking is only one possible form of static analysis. And the goal of static analysis is to detect errors as soon as possible. Dynamic typing is used more to perform dynamic type dispatch (that is, runtime polymorphism). Because they have such different goals, why are we even comparing them?
I think a lot of it is C++’s fault. C++ took the idea of Classes and inheritance from OOP and conflated them into static types. But I’m not an expert. Maybe someone did that before C++. By the way, this is probably 90% of why people hate static typing. When static typing prevents something useful (like duck typing) but does not present an alternative to that use, you wish you did not have static typing.
The real point is that static typing and dynamic typing are not inherently mutually exclusive. For instance, Java has static and dynamic typing. That is, you can ask for the type of an object at runtime, and the types of variables are checked at compile time. Clojure has some form of static analysis (no static type analysis) and inherits the runtime types from Java.
The question "which is a subset of the other?" is actually meaningless. But the question "if you had to choose a language that only had one, then add the other to it, which would you pick?" is becoming increasingly relevant. Haskell recently added what some consider to be dynamic typing behavior (throwing type errors at runtime instead of at compile time). And people are working on static type systems for Clojure.
I do not think that this is a subjective question. I think it has one possible answer in the general case. And I would like to present the argument to open up the discussion. I will use Haskell (with the most advanced static type system in the world) and Clojure (it is comparable to Haskell and dynamically typed).
In Clojure, type information is purely informational. It does have Java’s inheritance hierarchy, but that is a mere legacy to interoperate with Java. The important part of Clojure is that every value has a type and the program can know the type at runtime. This could be considered the minimal possible dynamic type system: present types at runtime and defer to the program to do what it wishes with them. The semantics of evaluation are independent of the type system.
In Haskell, type information exists only at compile time. That is, the compiler figures out the type of everything, decides whether to allow the program, then compiles it (if it is allowed). Types are complected with semantics. Types determine what code is run. Further, because the type checker has to be able to infer all types, the language has been limited to the subset of possible programs that are checkable. The semantics of the language are limited by the type system.
So what we have is a semantics that is independent of types (Clojure) which we can augment with different static type analyses and potentially catch errors, or we have a language that has already compromised its semantics to the type system and add runtime type annotations, which are effectively useless.
Just one last word: I am not a dynamic typing zealot. I use Haskell more than Clojure. And I am very excited to see static analysis come to Clojure. However, the static typing crowd seems to have the rhetorical upper hand at the moment. I just want to have better discussions.