My impression is that these type system limitations start showing up once you need to mix and match different type systems. For most terms there is a reasonable type system that will type it but when you scale up to your whole program/system then any single type system might not be able to cover all the terms that you want to write. Some type system features such as parametric polymorphism, subtyping, intersection types, linear types, etc are hard to mix in practice, especially when you worry about decidability and type inference.
I think this is one of the reasons why dynamic languages are popular. It kind of lets you mix different programming styles in a single program, at the cost of laxer type checking (something I hope will get better in the future).
No, this is not true at all. In practice every program is typed. There is no useful function that is fully general, so in practice there are limits on the types and the values of the inputs.
All dynamic typing gets you is that you can ignore this fact, and then hit it head on when you make a mistake.
It is possible to give programs that are truly dynamically typed, but they are not "real" programs. What can you do to a variable with zero information on what it contains ? Not much : return it, pass it around, ... that's about it.
The real complaints are mostly about two things
1) it's hard to know what type an input parameter should be, so I just don't. This is just not a valid complaint. But type inference helps a lot here. Generic programming catches the other usecases.
2) many languages make it really hard to correctly specify types. This is a language flaw. Dynamic typing can be an improvement over C code, but not over Haskell code. Other languages are somewhere in between.
Sometime, it's another flaw that may exist : the standard libraries are not correctly thought out to have a good algebraic structure (e.g. correct IS-A relationships for all number types, and the ability to generalize over them)
These complaints basically boil down to people having constructed wrong type systems. It's a flaw in many, many languages. Python and Haskell are the only ones I know to get it close to right.
3) You want to eval() in your program (or, for example, read in arbitrary json). If you're doing this, you're doing something wrong. Even if you do manage to isolate things correctly (history says you are unlikely to get this right), you're open to trivial DDOSing.
The problem in Python is that type errors end up being reported later than they would in a typed language. For example, if I have a function f that expects an object and call it with f(None) its not going to immediately give me an error. Its only going to give an error when I try to call a method on that `None` and depending on how my program is structured the line with the erroneous f(None) call might not even end up appearing in the final stack trace.
I think this is one of the reasons why dynamic languages are popular. It kind of lets you mix different programming styles in a single program, at the cost of laxer type checking (something I hope will get better in the future).