> all the people still arguing in favor of dynamic typing at all, are tilting at the windmill of static languages without type inference
Some of us who argue in favor of dynamic typing have a much more nuanced and informed view...
I, for one, am of the mind that the there are precisely zero production-caliber statically typed environments that possess a sufficiently powerful type system for the kinds of problems I tackle on a regular basis. Haskell doesn't count, since you need to turn on about a dozen GHC language extensions in order to incorporate the last 20 years of research. There's also quite a bit of design warts that newer academic languages are starting to iron out. In particular, I don't think monad transformer stacks are a reasonable solution to computational effects.
That's not to say you can't write any program in an environment where the type system is constraining you. You can. You simply implement a "tagged interpreter", which is something that's so simple to do, people do it all the time without realizing. Either you have a run-time map or you pattern match on an sum type data constructor, then loop over some sequence of those things with a state value threaded through. Poof! You've got a little interpreter.
I find that this happens a lot. And, I also find that a lot of problems are easier to reason about if you create a lazy sequence of operations and then thread a state through a reduction over that sequence. Now, in Haskell, I've got a type correct interpreter for an untyped (unlikely turing-complete) language! Sadly, I can't re-use any of the reflective facilities of the host language because my host language tries to eliminate those reflective facilities at compile time :-(
I'm in favor of optional, pluggable, and modular type systems. I think that a modern dynamic language should come with an out-of-the-box default type system that supports full inference. If, for some reason, I build a mini interpreter-like thing. I should be able to reuse components to construct a new type system that lets me prove static properties about that little dynamic system. This level of proof should enable optimizations of both my general purpose host language and my special purpose embedded "language".
Additionally, I require that type checking support external type annotations, such that I can separate my types from my source code. In this way, type checking becomes like super cheap & highly effective unit tests: The `test` subcommand on your build tool becomes an alias for both `type-check` and `test-units`. You just stick a "types/" directory right next to your "tests/" directory in your project root. Just as a stale unit test won't prevent my program from executing, neither will an over-constrained type signature.
> Haskell doesn't count, since you need to turn on about a dozen GHC language extensions in order to incorporate the last 20 years of research.
I don't follow. Turning on a language extension is as simple as adding a single annotation to your source file. Is this a problem?
Are you objecting to "the last 20 years of research" not being part of the definition of the Haskell language standard? This is a little off the mark, as Haskell was conceived in 1990, and the latest version of the standard is from 2010.
Moreover, Haskell is evolving rapidly precisely due to the use of language extensions. Research is done, papers are written, and extensions are added — then validated through practical use, and either kept or removed.
> There's also quite a bit of design warts that newer academic languages are starting to iron out. In particular, I don't think monad transformer stacks are a reasonable solution to computational effects.
Granted, monad transformer stacks can get unwieldy. Fortunately, writing in this style is not required. Monad transformers are just library code, so it's not necessary to invent a new language to replace them.
> Haskell was conceived in 1990, and the latest version of the standard is from 2010
Haskell was released in 1990, but the designs started on Haskell itself in 1987 and were heavily based on prior languages; standardizing on a common, agreeable subset. The fact that there is a 2010 version of the spec provides zero insight into how much the language has evolved over that 23 year period. That's not to say it hasn't evolved, just that it's silly to pick on my obviously hyperbolic trivialization of 20 years of progress.
> Haskell is evolving rapidly precisely due to the use of language extensions
Sure. And the fact that it is still rapidly evolving, especially in the type system department, is proof that there are interesting classes of problems that don't fit in to Haskell's type system in a sufficiently pleasing way.
Evolution is a good thing & I have a ton of respect for both Haskell & the PL research community. See the rest of my post for how I'd prefer an advanced language/type-system duo to work in practice.
> "there are precisely zero production-caliber statically typed environments that possess a sufficiently powerful type system for the kinds of problems I tackle on a regular basis"
This tells us nothing.
> "...since you need to turn on about a dozen GHC language extensions in order to incorporate the last 20 years of research..."
So it's a bad thing that you can turn off parts of language that you don't like? Also, don't I need to make an effort to go and download the latest version of e.g. Python just to get "the latest 21 year of research" (yes, it's that old)?
That's an apples and oranges comparison. Python's type system doesn't restrict me in the same ways a static type system does. There are Haskell programs that were not valid that are now valid if you enable a compiler flag and change something in the type sub-language as opposed to changing something in the term sub-language.
Some of us who argue in favor of dynamic typing have a much more nuanced and informed view...
I, for one, am of the mind that the there are precisely zero production-caliber statically typed environments that possess a sufficiently powerful type system for the kinds of problems I tackle on a regular basis. Haskell doesn't count, since you need to turn on about a dozen GHC language extensions in order to incorporate the last 20 years of research. There's also quite a bit of design warts that newer academic languages are starting to iron out. In particular, I don't think monad transformer stacks are a reasonable solution to computational effects.
That's not to say you can't write any program in an environment where the type system is constraining you. You can. You simply implement a "tagged interpreter", which is something that's so simple to do, people do it all the time without realizing. Either you have a run-time map or you pattern match on an sum type data constructor, then loop over some sequence of those things with a state value threaded through. Poof! You've got a little interpreter.
I find that this happens a lot. And, I also find that a lot of problems are easier to reason about if you create a lazy sequence of operations and then thread a state through a reduction over that sequence. Now, in Haskell, I've got a type correct interpreter for an untyped (unlikely turing-complete) language! Sadly, I can't re-use any of the reflective facilities of the host language because my host language tries to eliminate those reflective facilities at compile time :-(
I'm in favor of optional, pluggable, and modular type systems. I think that a modern dynamic language should come with an out-of-the-box default type system that supports full inference. If, for some reason, I build a mini interpreter-like thing. I should be able to reuse components to construct a new type system that lets me prove static properties about that little dynamic system. This level of proof should enable optimizations of both my general purpose host language and my special purpose embedded "language".
Additionally, I require that type checking support external type annotations, such that I can separate my types from my source code. In this way, type checking becomes like super cheap & highly effective unit tests: The `test` subcommand on your build tool becomes an alias for both `type-check` and `test-units`. You just stick a "types/" directory right next to your "tests/" directory in your project root. Just as a stale unit test won't prevent my program from executing, neither will an over-constrained type signature.