From anecdotal experience I view OCaml's bytecode compile speeds to be on par with Go's (as a rule of thumb I expect about 1 second per 10k lines). Far slower than something like TCC, but quite fast among other languages.
As for your view of OCaml byte code compilation speed being on par with Go seems rather unlikely or I'm misunderstanding what you mean. Go is not dealing with anywhere as near a complex type system as OCaml and generates a lot more potato machine code (usually not using -mnative so you can know you can copy a binary to arbitrary machines).
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.
I think this is due to the influence of Pascal and/or Modula on the language.
If you're asking about OCaml's pattern matching, it's compiled quite efficiently: https://www.cs.tufts.edu/comp/150FP/archive/luc-maranget/jun...
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.
To be clear, I think pattern matching is a good thing.
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.To add some data to the discussion, I have been recently measuring the compilation profile of some OCaml libraries, and the average time spent in the typechecker is around 40% (https://www.polychoron.fr/ocaml/2021/08/19/measuring_compila...).
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.
The compiling pipeline of bytecode is basically typing -> lambda -> bytegen
Bytegen is really close to what Zinc does.
But doesn't the compiled code run slower in bytecode mode? I think that counts.
The healthy mix of interpreter/JIT/AOT backends is what many languages miss on their toolchains.
Making so that a project with 20k loc is less than a second in debug mode. And recompile time in native(debug) mode is almost 100% linking time.
For bytecode you can have wild rebuild times, in the milisecond range.
$ time make -C source -j8
make: Entering directory '/home/fnord123/src/download/pyre-check/source'
../scripts/setup.sh --configure
Build info: Linux x86_64 @ Mon Aug 23 2021 (development build)
abort: no repository found in '/home/fnord123/src/download/pyre-check/source' (.hg not found)!
Git commit: 1e4dd5a39db1631fd13a3a8df74d6e0f5d801fbc
dune build @install -j auto --profile dev
menhir parser/generator.{ml,mli}
Warning: 31 states have shift/reduce conflicts.
Warning: 5 states have reduce/reduce conflicts.
Warning: 77 shift/reduce conflicts were arbitrarily resolved.
Warning: 75 reduce/reduce conflicts were arbitrarily resolved.
make: Leaving directory '/home/fnord123/src/download/pyre-check/source'
real 0m43.700s
user 4m3.030s
sys 0m41.005s
I must be doing something wrong.[1] https://github.com/facebook/pyre-check [2]
$ tokei
-------------------------------------------------------------------------------
Language Files Lines Code Comments Blanks
-------------------------------------------------------------------------------
Autoconf 2 235 204 3 28
C 15 242641 165361 60669 16611
C Header 10 14267 2617 11094 556
Coq 10 2391 1968 220 203
CSS 5 713 540 44 129
Dockerfile 2 71 42 11 18
HTML 2 35 30 0 5
INI 1 12 9 0 3
Java 31 2654 2080 267 307
JavaScript 7 867 752 61 54
JSON 17 939 939 0 0
Makefile 5 229 145 45 39
Markdown 47 7213 7213 0 0
OCaml 791 229096 198302 10455 20339
Python 398 61909 50119 2917 8873
Shell 5 111 59 34 18
SVG 2 2 2 0 0
TeX 4 929 642 129 158
Plain Text 5 457 457 0 0
TOML 1 2 2 0 0
TypeScript 2 130 84 20 26
-------------------------------------------------------------------------------
Total 1362 564903 431567 85969 47367
-------------------------------------------------------------------------------Reading your table, there are 198302 lines of OCaml code. That's about 10x larger than the 20k you claim. The 20339 in that row is the count of blank lines. I doubt they matter much here.