Good points!

1: That's true for anything which isn't a primitive type, or built in to the language, sure. Most of the time though, the type you have to define is not exactly complex, take records for example (using a Haskell-ish syntax):

data Person = {name: String, age: Int} deriving (Eq, Show)

That's something not only easy to define, but something that you'd _want_ to define anyway even in dynamic languages, i.e. via `defstruct` in Elixir, or `class` in Ruby/etc. The language (at least in Haskell, and in the language I'm working on) can help you derive the common interfaces you'd want (structural equality and inspection/stringification in the example), and then that's it, type inference takes over from there. In Haskell/ML, you typically only need type annotations when working with higher-ranked polymorphism (e.g. functions which operate on polymorphic functions), and you can go a long way without needing to write code like that. In other words, the burden at this level is extremely lightweight, and I would argue that it imposes no more burden than you would already undertake in well-written dynamic code (i.e. defining structures/classes).

Once you get into a place where you are writing abstract code to work across types, with no type constraints, where higher-rank polymorphism is likely to be front and center, then yes, you will spend some extra time up front annotating those functions with their polymorphic type, and that can be annoying sometimes. Even that's not a given though, in Haskell for example, one can define a typeclass (such as Eq, for equality), and write code which operates on instances of that type without needing to do anything more than:

foo :: (Fooish a) => a -> a
foo a = # do something Fooish with 'a'

That function is polymorphic across any type which is an instance of `Fooish`. I mean, I guess one could consider that an unbearable burden, but to me that's at least as much "extra" as I would do in Elixir with typespecs, and in another language via function docs at a minimum. Writing abstract code like that in a dynamic language probably still requires you to perform some checking of the arguments to ensure that they are actually of a type that is valid for the function.

You can go into production writing code without your compiler proving that what you expressed to it is valid, but if something is wrong, you don't have any tooling (short of debugging) to fall back on, you have to figure out what went wrong, try to fix it, deploy again and hope you actually fixed the problem. With (good) static type systems, you may be slower to production that first time, but the compiler has ensured that you haven't made any mistakes based on what you told it you were trying to do. One can of course still make mistakes with a type system helping you, but at that point it's a conceptual mistake, i.e. you've told the compiler to do A, and it did A like you asked, but the right thing was actually B.

Nevertheless, I understand the desire for a compiler to just do what I say, regardless of whether the compiler thinks it's ok; but most of the time when I do that, it means the next person working the code behind me isn't going to understand it - if it's not clear to the compiler, it's probably going to be unclear to a reader of the code too. That isn't a hard and fast rule, but I have certainly seen instances of that.

In my experience, most of the dynamicism I rely on in such languages is for metaprogramming, all of which I would rather do at compile-time via macros anyway, and thus doesn't require a dynamic type system. For everything else, I'm having trouble thinking of an example where types would interfere, other than writing code which ignores things like checking if a result exists or not, or is a datastructure vs an error - and I would rather have the proper handling of those things done and enforced by the compiler rather than ignore it until something blows up in production. That's my take though, and for those who are desperate to get something in to production ASAP, it's probably not the right fit.

2: I'm not sure that working with arbitrary data leaves you in a situation where you are no longer within the realm of safety the compiler can provide you. You will have code that cares about aspects of that data, imposing some kind of logical structure to it, whether that's encoded in a type or not. The only difference between dynamic and static processing of that kind of data is that the dynamic code has to search for the structure it expects, while the static code defines that up front, and only has to try to match the data to that structure - working with it after that point is not going to differ much between the two. With dynamic code you'll still need to check if a field is present or not, and whether it's of the type you expect it to be. It is certainly easier to create arbitrary heterogeneous structures in a dynamic language (I won't argue that!), I don't buy the argument that a static type system is somehow ill-suited to the processing of such data.

I will say that it is annoying that before you can start writing code to process the data in a static type system, you have to define the types/interfaces that the data will take - sometimes I would rather just mold the raw datastructure via traversing it and pattern matching on it - but I think in the end that extra time up front ends up a wash with a dynamic type system because you spend less time on the tail end chasing bugs that the compiler can catch.

For Rich's argument to resonate with me, I think I just need a good example of what he's talking about - every time this topic has come up, I haven't been able to come up one, and I haven't seen anybody else do so either. An example where Rich (or whoever) thinks the dynamic solution is clearly superior, and no static solutions come close.

At the end of the day, I say use the right tool for the job, and if a dynamic language clearly solves a problem better than the static one, then hey, I'm not complaining (after all, it's why I'm using Elixir/Erlang to solve problems, though I don't think the problems they solve are necessarily at the dynamic/static divide).