> I'd point out though that ML & Haskell's type systems might be simpler from a theoretic stand point, the ugly Java model appears to be easier for humans to reason about.

It's easy to reason sloppily about anything. It isn't too hard to write ML programs where types are just as uninformative as in Java, but I don't do this because it isn't helpful.

> You make it sound like it is trivial to validate those static guarantees in Python. Have you built some trace framework for Python that validates all the types that can be passed to a particular function? That'd be super handy.

When I write Python programs, I only care which operations can be used on an object, hopefully without raising an exception. I'm perfectly aware that it's unreasonable to expect more than this. The situation isn't much better in Java.

> I wasn't talking about code generation.

Me either.

> There's a whole bunch of code analysis/interpretation logic that is much harder to achieve in Python. For the most part, the IDE's that support both just provide a watered down Python feature set.

I don't care about the questions a language or IDE can answer. I care about the questions I need to ask, which are driven by the problem domain, rather than the capabilities of the language. If they happen to coincide, good for the language and good for the IDE.

> You don't have to model that fact as a distinct type in the type system... and people often don't. Often, people implement business logic in code, rather than in the type system... else you couldn't get anything done with JavaScript (which I'm told is not actually the case, despite appearances ;-).

You're right, I don't need to model it with types. But if I'm using types, they better be useful for modeling purposes! And if they aren't useful for modeling purposes, they better stay out of the way.

> Yup. No composition. Then again, you can handle that logically in other ways. In the case of a TreeMap, the right to handle it would be to have a way to access the Map.Entry associated with a key.

Yep, this sounds like the Right Way (tm) to do it, given the constraints. Unfortunately, it's not the path of least resistance. It's the language designer's job to make these two align.

> You sort of have that, but it is all around floor/ceiling/range type queries, rather than the get because... well, you rarely need the feature and you can get by with using the containsKey() test.

Which is an admission that types aren't helping.

> The nice thing is that if I can't remember if get() returns the Map.Entry or just the value, the type system can catch me on it, because the return types are indeed different. In Python, I'd actually have to test it or look at the documentation.

Yep, types catch quite a few things in Java. But they don't catch enough things for it to be worth the annotation burden. Crucially, they don't catch `UnsupportedOperationException`.

> That's semantics for the same thing as saying the variable can point to any type. That "single static type" can be tied to any Python type, making all variables intrinsically maybes... just like all of Java's reference types.

Python variables aren't maybes. Maybe has only two constructors: Just and Nothing. Python expressions are classified by a gigantic sum type that can even be extended with new constructors at runtime.

> You can totally know whether you've properly handled all the null cases.

Most interesting case analysis aren't of the form null vs. not null.

> There's lots of tools that do exactly that, even taking advantage of the @NotNull attribute to help winnow down the possibilities.

These tools are cumbersome, workflow-disrupting, and even then, sometimes they still fall short. For example...

> For more complex case analyses, you can use Enum's & EnumMap's to know if you have complete coverage. It can be done through static analysis... just not purely from the type system.

... how would I say “list of lists of animals, where each sublist only contains animals of the same species”? (Assuming each species is a separate class.) In Haskell (plus existential quantification, a fairly uncontroversial extension) or OCaml (with GADTs, which subsume existential quantification), this is a breeze.

> You're ignoring all the other cases where types do help, because the type system doesn't help with your pet case.

Given the ubiquity of case analysis in programming, supporting it in a disciplined way isn't a “pet case”, but rather an everyday necessity. Even dynamic languages offer good facilities for it (Racket, Erlang).

> In short, when it comes to static typing, you throw out the baby with the bath water.

Nah. I know how to settle for less than what I want. For example, safety leaves a lot to be desired in C++, but it has other nice things to make up for it, like the previously mentioned compile-time dimensional analysis example.

> I get it, you don't like it, and it is incredibly annoying looking at that narrow uncanny valley to the thing you do like, but let's not rationalize it by asserting it doesn't help.

I love static type systems. That's why I feel insulted when they try to sell me a glorified linter as if it were a type system.