It's sort of a myth that programmers don't have to worry about types in dynamically typed languages.
In dynamically typed languages:
You still have to know if you're working with an array, an integer, a string, a hash table, a function reference, a dictionary, an object, or whatever.
If it's an object, you have to know what class it belongs to.
Assigning one of these types to a variable or function parameter expected to be another type is almost always an error.
At a lower level, things like number of bits or signed versus unsigned frequently still must be accounted for if you are populating a TCP packet, for example.
You can run into problems where you get a zero where you really wanted an empty string. In other words, you're still debugging type mismatch bugs. The only real difference is the compiler isn't catching the errors.
I'd argue that you aren't even saving much typing- , because you tend to want to document in comments what type your function parameters are instead of documenting it in your code. This is why doxygen-style comment blocks are much more popular in practice throughout dynamically typed code, where in statically typed languages you mostly only see them for libraries.
That's not to say that programming in dynamically typed languages doesn't feel more pleasant because the compiler isn't always on your back, and experienced programmers don't tend to have difficulty finding and correcting the kind of bugs that static typing would catch anyway, but that's a completely separate issue from an alleged increase in efficiency or reduction in bug rate, for which dynamic typing is at best even with static typing.
An interesting case study on the matters of scaling projects that use dynamic and interpreted language can be found in Beginning Scala by David Pollak.
I started searching for a way to express the code in my brain in a simpler, more direct way. I found Ruby and Rails. I felt liberated. Ruby allowed me to express concepts in far fewer lines of code. Rails was so much easier to use than Spring MVC, Hibernate, and the other “streamlined” Java web frameworks. With Ruby and Rails, I got to express a lot more of what was in my head in a shorter period of time. It was similar to the liberation I felt when I moved from C++ to Java...
As my Ruby and Rails projects grew beyond a few thousand lines of code and as I added team members to my projects, the challenges of dynamic languages became apparent.
We were spending more than half our coding time writing tests, and much of the productivity gains we saw were lost in test writing. Most of the tests would have been unnecessary in Java because most of them were geared toward making sure that we’d updated the callers when we refactored code by changing method names or parameter counts. Also, I found that working on teams where there were mind melds between two to four team members, things went well in Ruby, but as we tried to bring new members onto the team, the mental connections were hard to transmit to new team members.
I went looking for a new language and development environment. I was looking for a language that was as expressive as Ruby but as safe and high-performance as Java...
As you can see, major challenges in project scaling for author turned out to be in test development and knowledge transfer.
In particular, author goes into more details in explaining the differences in test writing between dynamically and statically typed languages in Chapter 7. In section "Poignantly Killing Bunnies: Dwemthy’s Stairs" author discusses Scala port of a particular Ruby example:
Why the Lucky Stiff... introduces some of Ruby’s metaprogramming concepts in Dwemthy’s Array in which a rabbit battles an array of creatures. N8han14 updated the example to work in Scala...
Compared to the Ruby code, the library parts of the Scala code were more complex. We had to do a lot of work to make sure our types were correct. We had to manually rewrite Creature’s properties in the DupMonster and the CreatureCons classes. This is more work than method_missing
. We also had to do a fair amount of work to support immutability in our Creatures and Weapons.
On the other hand, the result was much more powerful than the Ruby version. If we had to write tests for our Ruby code to test what the Scala compiler assures us of, we’d need a lot more lines of code. For example, we can be sure that our Rabbit could not wield an Axe. To get this assurance in Ruby, we’d have to write a test that makes sure that invoking |^
on a Rabbit fails. Our Scala version ensures that only the Weapons defined for a given Creature can be used by that Creature, something that would require a lot of runtime reflection in Ruby...
Reading above can make one think that as projects grow even larger, test writing might become prohibitively cumbersome. This reasoning would be wrong, as evidenced by examples of successful very large projects mentioned in this very question ("Python is successfully used for... YouTube").
Thing is, scaling of the projects isn't really straightforward. Very large, long-living projects can "afford" different test development process, with production quality test suites, professional test dev teams and other heavyweight stuff.
Youtube test suites or Java Compatibility Kit sure live a different life than tests in a small tutorial project like Dwemthy’s Array.
Best Answer
That's not the definition of statically typed. Statically typed means that type checking (and type inference) happens before runtime. The opposite is dynamic typing, where type checking happens at runtime.
It is perfectly possible to design a statically typed language in which either identifiers can change types or types can change.
There is no universally accepted definition of type safety. Yours is a very sensible one.
There is no such thing as an interpreted statically typed language, simply because there is no such thing as an interpreted language, period. Interpretation and compilation are traits of the, well, interpreter or compiler (duh!), i.e. the implementation, not the language. Every language can be implemented with an interpreter and every language can be implemented with a compiler. In fact, many languages have both interpreted and compiled implementations. For example, Haskell has several compiled implementations (Ghc, Jhc, Yhc, Lhv) and an interpreted implementation (Hugs). ECMAScript has pure compiled implementations (V8), and hybrid mixed-mode implementations (e.g. SpiderMonkey AOT-compiles ECMAScript to SpiderMonkey bytecode, then both compiles and interprets this bytecode)
Saying that a language is an "interpreted language" is not just wrong, it is even more than wrong, it is simply non-sensical. The idea of "language" and the idea of "interpretation" live on two different levels of abstraction. If English were a typed language, "interpreted language" would be a type error.
You are inconsistent with yourself here.
You say "statically typed" means "does not allow to change the type of a variable", which means you can not write such code. But now you say that you can write such code in a statically typed language.
But if you can write such code, then by your own definition, it is not statically typed. And if it is statically typed, then by your own definition, you cannot write such code.
So, it's not surprising that you get a contradiction, because you are contradicting yourself.
Whether or not type safe languages can only be statically typed or not really only depends on how you define "type safe". Another common definition of "type safe" is that you cannot subvert the type system. Ruby is dynamically typed, but it is usually described as type safe. On the other hand, C is statically typed but nobody would describe it as type safe.
As for whether a type safe language can only be compiled, I already explained above that compilation is an implementation detail, it is not a property of the language itself.