Number of hours I've spent fixing type-related bugs in Python code = n
Number of hours I've spent figuring out how to make the compiler happy in C# ~= n * 5
I'm not being altogether serious (I've written a lot more Python than C# so it's not a fair comparison) - but you're ignoring a lot of useful debate about static vs dynamic, explicit vs implicit, type inference, languages for teams vs languages for hackers etc etc. It's fine to not go down the rabbit hole but at least acknowledge there is some debate on this topic.
Otherwise you give the impression that you think those of us who prefer dynamic languages are deluded oafs we are going to wake up one day, slap our foreheads and exclaim "Doh! It's only just occurred to me how useful explicit typing is. Silly me..."
EDIT - I've always enjoyed this post to python-dev by Cory Benfield explaining why he disliked the idea of introducing type annotation to the requests library.
Whilst it's not an indictment of all possible type systems it does give some flavour of how the mind sets differ in static and dynamic language communities. There is a genuine cost in expressiveness that surely can't be entirely dismissed out of hand.
I'd just to like to point out that grouping together dynamically typed languages and comparing them to statically typed ones as a whole is really not useful at all due to the huge variations within each group in terms of correctness and expressiveness.
When people compare dynamic vs static languages they generally compare the languages they're familiar with and often assume that they are representative of the group to a sufficiently high degree. They're not. Using Clojure for example is a very different experience from using Python and not just cosmetically. Clojure with spec is a very different experience from plain Clojure. Haskell is very different from C#.
I'd encourage anyone with an interest in using the best tool for the job and increasing their effectiveness as developers to at least dabble in what I would consider to be the state of the art in practically usable, more or less mainstream languages in both camps - Haskell and Clojure (with an extensive use of spec).
Without a good unit test suite you are doomed in any language.
I actually think that writing bug-free code (ahem!) is most easily done in Python, because it is just much easier and faster to debug, test and fix.
Java is pretty okay too and the code fast. I would choose Java for any large project that does not have deal with string processing. Although it is still much slower to develop than Python, it has the best technology stack ever!
Well, when you get your C++ program to compile, it is still probably wrong. Only after running with Valgrind to check for problems during runtime I could have some confidence that it is working.
I would choose C++ only for specific algorithms and data structures that I need to be very efficient. One major advantage for C++ is that it is very convenient to interface it from any other language.
That said, note that I only compared fairly unfraily these three languages and based my opinions on my skills and experience. Still, I could bet on it that if you were write any program in Python vs C++, the Python program not only gets completed much faster, but it also has less bugs.
I think if you really want to participate in the "strong vs. dynamic type" conversation, you need to pick something like Rust or Haskell for your strong typing. Mere "manifest" typing is a very weak type of strong typing. It can be coerced into giving some nicer guarantees via some trickery with things like constructors setting private fields and validating them at the time. For people who advocate proactively preventing bugs via type systems, the type system that C++ or Java has is not very good at it.
Even Go v. Python is a more interesting comparison than you might initially think. I prototype things in Go pretty much as quickly as I could in Python now, but the prototypes are way easier to transform into production code after the first couple of thousand lines. In fact, given how much time I spend with network servers and concurrency I am probably prototyping more quickly than I could in Python, and still having an easier time converting to production services. Go also has a terribly weak type system from the point of view of those who wish to proactively prevent bugs with them (weaker than C++ and Java), but since it has GC and array bound checks it grabs a lot of the low hanging fruit.
(See https://www.joelonsoftware.com/2004/06/13/how-microsoft-lost... , under the heading "Automatic Transmissions Win The Day".)
Guido Van Rossum seems to be more enthusiastic about static type checking. He's put in a lot of effort recently in to mypy, a static type checker for Python.
Dropbox, the company that Guido works for, has committed to adding static type annotations to their entire 4,000,000 line codebase and checking it with mypy. As of 2016 they had done 400,000 lines, and 700,000 lines as of 2017 (after 18 months total).
Guido also mentioned that Dropbox's desktop client team, one of the most important teams at Dropbox, has mandated that all new code have static type annotations and be checked with mypy.
 - http://www.mypy-lang.org/
 - https://www.youtube.com/watch?v=ZP_QV4ccFHQ
 - https://www.youtube.com/watch?v=7ZbwZgrXnwY
I am watching  at the moment and I would just like to give my perspective on how I would approach this problem.
To rehash, the question in the video is that just by looking at the following code:
for entry in entries:
The way I've been solving these situations is that I look into the unit test of the relevant function to understand what is going on. Unit tests also act as concise examples on how to use the code with example data. Also, if necessary, I can just debug the relevant unit test and step through the function and see in real-time what is going on.
But I am not trying to claim that type annotations are a bad idea, I just haven't used them myself yet.
> Without a good unit test suite you are doomed in any language.
I write a lot of code that is difficult to meaningfully unit test. How do I properly unit test a platform agnostic wrapper for gamepads - hook up a raspberry pi to some servos? How do I share that with our build server cluster? When is it a bug if nVidia and Intel GPUs render my scene slightly differently - what specific fuzzy conditions qualify as "bug" vs "expected variation"? How do I catch a missing memory barrier in multi-threaded code with unit tests? Sometimes an ARM processor with it's weaker memory semantics can help, but that still won't catch cases where triggering the bug requires the optimizer to do something specific.
> Well, when you get your C++ program to compile, it is still probably wrong.
While I agree in general, there are counterexamples. Even in the tire fire that is C++, static typing and compile time checks mean that if it still compiles, my rename refactoring in a large project was probably successful. If I rename something in Python, I have no such assurance, and probably updated the name where it was actually referencing something else, or failed to update the name where I thought it was referencing something else and it wasn't. So I have to test "everything". If I'm lucky, my manual audit of the codebase will show that this thing was only used by things that I know are well covered by unit tests. But let's be honest, I'm not that lucky.
Fiddling around with (often unsound) type systems, writing mocked tests, lazy/unmotivated reviewers and QAs these are all things that will cost time and money...
Hmmmm. Several decades of nuanced debate summarised inaccurately in one neat little hand-wavy paragraph.
I agree. I didn't say otherwise. The passage I quoted said "prefer using languages with type systems for writing code that needs to be correct" which seemed fairly sweeping to me. Those "other things" you mention sometimes matter a lot.
I also to a lesser degree take issue with "If your language does not have a type system, defensively check that the data you’ve been passed is correct at runtime, and throw an error if it isn’t." - this seems like a fairly overly strict guideline. Most tests will check types as a side-effect of whatever they are doing. And the advice to add runtime type guards into every piece of code I write is worse than telling me to just switching to a explicitly typed language.
This kills readability. Stuff like that belongs in tests. Code should express the intent and logic and anything extraneous should be abstracted away or not be there at all. Code should aspire to reading like pseudocode.
If it is publicly exposed, you are more likely to need to validate assumptions (if they aren't statically enforced), otherwise you are prone to creating undefined behavior.
I just looked through a few stdlib functions and other popular libraries and I'm not seeing much trace of validity checking as a standard working practice:
> from random import randint
> randint(1, "strawberry")
TypeError: cannot concatenate 'str' and 'int' objects
This aligns with Python's encouragement of duck typing but directly contradicts what you seem to be claiming as a universal best practice?
I still feel I'd prefer the occasional inconvenience in tracking down a bug over some of the pain I've experienced in trying to adapt my brain to a statically typed language. As another poster commented it really only makes sense to compare a cutting edge dynamically-typed language with a cutting edge statically-typed language if you want to debate the strengths and weaknesses of each. Otherwise you're just comparing specific language weaknesses and strengths.