Dart had gradual types but didn't enforce them at runtime because of performance. The PyPy devs don't believe that type annotations help them for performance (http://doc.pypy.org/en/latest/faq.html#would-type-annotation...). Also there is no JS engine that uses TypeScript annotations so far to improve performance.
Types are usually on the wrong boundary: e.g. Integer doesn't state whether that value fits into a register or is a Bignum.
Also: Aren't some type checks quite expensive? So more expensive than a simple map/class/shape check? E.g. passing an array from untyped code to a signature with something like `Array<Integer>`. Wouldn't a runtime that verifies signatures have to check all elements in the array to be actual Integers?
To me, it feels that there is a very thick wall in between high level languages and something with raw data access like C, C++, and D. You either completely throw out every convenience feature, or go all in on them.
In C, a lot of data access turns into single digit number of load/store or register access instructions. It is easy to see that it is close to impossible to add fancy data access functionality on top of that without going from single cycles to kilocycles.
I was once told "when your try improving a programming language performance, it eventually turns into C"
P.S. on JIT - it is not given that a JIT language be automatically faster than a well written interpreter on a modern CPU. One of early tricks of making fast interpreters was to keep as much of interpreter in cache and data in registers as possible to benefit from more or less linear execution flow of unoptimised code in comparison to unpredictable flow of JIT made executable code. Today, with 16MB caches, I think the benefit of that will be even bigger.
Which is kind of ironic given how bad C compilers generated code during the mid-80s, versus other mainframe languages.
That is why we need something that offer 80% the Speed of C, 80% of Simplicity / expressiveness of Javascript / Ruby, and 80% of ease of long term maintenance of a functional PL like Ocaml.
I actually think Java will one day evolve very close to that goal.
I doubt a competent JIT is ever slower than a competent interpreter, but it may not be that much faster or worth the workload.
It depends on the size of the primitives. An array language could be close to 1:1, while for a cpu-level instructions you will struggle to reach 1/6 of JITted perf.
One of the reasons why Sorbet does both runtime checking[1] more than just static checking is so that we can know that signatures are accurate, even when a typed method is called from untyped code.
If the signatures are accurate, a future project could take advantage of method's signatures to make decisions about how the code should actually be run. If the signatures lie, then any runtime optimization made using the types would only be overhead, because the runtime would have to abort the optimization and fall back to just running the interpreter.
[1]: https://sorbet.org/docs/runtime