I would say that dynamically-typed languages are great to prototype as they do not emphasize on being correct too much.
Being correct from day one will cause too much unnecessary friction.
You (usually) don't know the entire program architecture until you make a full prototype, and even if you have a plan, there will always be some part where some unexpected consequences force you to rearchitect some parts.
And since you don't know the entire program architecture you architect your program bottom-up, and most of the dynamically-typed languages allow an interactive development environment at a flexibility that typed languages can't provide.
Think about developing Python inside a Jupyter notebook, or Common Lisp inside a REPL.
You turn on a REPL, open up a file, write a function, send the function to the REPL, test the function you just wrote, and when I find a mistake I can just redefine any function I would like to change.
This process allows fixing mistakes on-the-fly. Typed languages don't allow this (even ones that have a so-called REPL) at this flexibility, (since they emphasize on being correct all the time), and cause too much unnecessary friction while prototyping.
Thus a need for a dynamically-typed language that can enforce types after prototyping.
Yes, it's this flexibility that allows you to experiment. Static typing proponents often say, since a type-error means that the code doesn't make sense, why would you ever want to run code that doesn't typecheck? But I find that when I'm experimenting, I want to do this all the time.
I ran into this recently when I was working with Rust. And don't get me wrong; I love Rust. But I was experimenting with a new way of doing things in my codebase and was adding a new kind of statistical analysis, and I wanted to run a test case to see what the result was... However, I couldn't because the compiler's typechecking refused to even compile my code, when I knew for a fact that the type errors had nothing to do with the code path of my test case.
I get that that's the whole point of static checking. I can change a large codebase and know exactly when/where I've broken something even if I'm not familiar with all the code, and it will barf at me. But the fact that I can outsmart it, even some of the time, leads me to believe that there will always be a place for dynamic languages.
If 99% of my code has type errors, but there is one code path that is true, who is the compiler to say that that isn't the one code path that I want to take? When experimenting, I build my code dozens of times. The vast majority of the time, I am the only one who consumes the build result. Once in a rare while, I will reach a steady state and build a release. It's really only then that I want the compiler to barf at me and refuse a broken build.
I must not be the only person on the planet who wants to do this sort of thing.
The eclipse java compiler can be instructed to substitute (most) compile errors with runtime exceptions for those intermediate builds. You still get all the compile time error messages, but the code is ready to run, right up to the point where the compiler complained.
It doesn't when the error is in something clever you did with the type system (e.g. wildly interdependent generics pretending to be scala).
But for many simpler cases (e.g. the majority of idiomatic java), it can be as simple as substituting an offending imperative line with a throw RuntimeException statement that parrots the compiler error message. I'm sue that they are doing a lot more than that.
It looks like what you want is type-mismatch as warnings. I'm not sure it would be viable though, because dynamic languages can throw an exception in this case, while static languages will segfault. Unless the compiler inserts instrumentation code where it saw a type mismatch?
Yea, that seems like a use-case I’ve felt like I wanted before. TypeScript supports this, as the JS output can be produced even if type checking fails, though in practice I have it turned off so that I don’t accidentally end up with type errors I’ve not fixed.
Statically typed languages that employ type inference (eg. Hindley–Milner) do exactly this, ie. you just write your function definition and the inference engine figures out its type. Doing this in the repl gives you comparable flow to doing it in dynamic langs but with full type safety.
Being correct from day one will cause too much unnecessary friction. You (usually) don't know the entire program architecture until you make a full prototype, and even if you have a plan, there will always be some part where some unexpected consequences force you to rearchitect some parts. And since you don't know the entire program architecture you architect your program bottom-up, and most of the dynamically-typed languages allow an interactive development environment at a flexibility that typed languages can't provide.
Think about developing Python inside a Jupyter notebook, or Common Lisp inside a REPL. You turn on a REPL, open up a file, write a function, send the function to the REPL, test the function you just wrote, and when I find a mistake I can just redefine any function I would like to change. This process allows fixing mistakes on-the-fly. Typed languages don't allow this (even ones that have a so-called REPL) at this flexibility, (since they emphasize on being correct all the time), and cause too much unnecessary friction while prototyping.
Thus a need for a dynamically-typed language that can enforce types after prototyping.