Hindley-Milner typechecking in practice is quite fast (as evidenced by ocamlc and Elm's compiler) and easily on par or faster than Go's typechecker (my rule of thumb for Elm code is usually 1 second per 100k lines).
In general HM-based type systems are both conceptually quite simple and can have quite fast implementations. What makes them seem "complex" is that languages with HM-based type systems also tend to make it hard to "go around" the type system and enforce much more rigid discipline around how your code can be written.
One thing that helps OCaml here is that it requires definitions to precede usage (like C/C++). Go and most other languages don't have this requirement. Perhaps, this results in faster symbol table lookups. Moreover, OCaml boxes everything and doesn't produce monomorphized code for generic types. OCaml's code generation is also much simpler than the likes of LLVM or GCC.
Apple autocorrects poorly. I meant “isn’t pattern-matching”. And I was asking about checking exhaustiveness at compile time specifically, not about the compiled code.
There's all sorts of terrifying exponential things that affect HM typechecking in theory (e.g. pattern matching exhaustivity checking as you mention is directly equivalent to SAT, although this is technically not a part of HM typechecking, it is a feature in practice for most HM-inspired languages).
In practice it is actually very rare that they significantly affect compile times because it is very rare for the `n`-size to increase as the size of your codebase increases (these exponential blowups are generally limited to a single expression) for non-pathological code. Basically while you can have exponential blowups inside a single expression, I can't think of a time where you'll have exponential blowups across multiple expressions, which is what counts in a large codebase.
One of the times where big-O analysis doesn't accurately predict real-world runtimes.
Go's type system is actually of significant implementation complexity (in its details it's actually probably even more complex than Elm although less so than OCaml). In a typical implementation of HM the only nontrivial step (i.e. that isn't just a lookup and then checking if two constants are equal to each other) is union-find, i.e. a couple of set operations, but Go also has set operations it needs to do for type checking interfaces. So there isn't a clear performance win at a high level.
I’m talking about computational complexity. Go might have more edge cases than Ocaml, but I don’t believe it doesn’t have to exhaustively check match statements, for example.
To be clear, I think pattern matching is a good thing.
Indeed I was talking about computational complexity as well there when it came to set operations.
Go and HM-style type systems both have set operations they need to do that are of comparable practical computational complexity.
Exhaustivity checking without pattern matching (e.g. inserting wildcard expressions in every argument to a pattern other than the outermost tag) is computationally very straightforward and is at worst linear in the number of constructors a given type has. This is similar in computational complexity to Go's type checking of interfaces, which is also linear in the number of methods (since Go doesn't have any explicit interfaces it cannot simply do a name lookup).
// This still checks for exhaustivity and is at worst
// linear in the number of `Case0`, `Case1`, etc.
case thing of {
Case0 x -> ...
Case1 y -> ...
Case2 z -> ...
}
// This is how you get equivalent to SAT, but is also
// comparatively rare
case thing of {
Case0 True False True -> ...
Case1 True True True -> ...
Case0 True True False -> ...
// etc.
}
Exhaustivity checking in the presence of pattern matching is only non-linear in the number of parameters to the constructor, which in most code is only one (because beyond one usually you would use a record instead). So in practice there aren't any obvious computational edges that Go typechecking has.
The HM type system is designed to be as fast and efficient as possible. And the OCaml typechecker is quite optimized (sometimes to the detriment of readability). Outside of GADTs heavy code, it feels unlikely to me that the exhaustiveness check matters much to the compilation time.
Thus the complexity of the OCaml type system can only have a limited effect on OCaml compilation time.
And this measure does include interface files, where the compilation pipeline is reduced to just parsing, typechecking, and dumping the computed file to the disk.
In general HM-based type systems are both conceptually quite simple and can have quite fast implementations. What makes them seem "complex" is that languages with HM-based type systems also tend to make it hard to "go around" the type system and enforce much more rigid discipline around how your code can be written.