This is very neat, I love the idea of using the syntax tree to guide what can be parallelized vs. not.
As some additional background, there's a very good reason why the F# language typechecks files sequentially. Because F# supports type inference at every scope, a single change in the body of a function can result in cascading type changes across an entire project. This kind of change can not only affect other files, but other files inside of other projects in the same solution.
A way to curb these kinds of cascading changes is explicit type annotations and/or signature files, which "lock" the type signature for a given construct/file. And the F# compiler has had optimizations in place when signature files are used to know when to re-check stuff based on this. However, these didn't extend to the sequential typechecking of files, and for the large majority of codebases that don't use signature files, these optimizations didn't really kick in anyways.
That's interesting. In Haskell top-level annotations are not required but I don't think I've ever seen a real project without them - its basically a community standard that's universally adopted, in a community that doesn't adopt many standards.
It's useful to keep these anyway, as if you make a change that accidentally changes the type of some function, the error message makes it immediately obvious what you did. Otherwise it's hard to tell whether you messed up the function or the caller.
Also having type annotations makes code reviews easier. Basically people's brains can use the same optimization that compilers do.
There is also a helpful compiler command to generate updated signature files for you. Then you just glance at the git changes and see if you accidentally broke an API or not.
May I plug https://github.com/G-Research/ApiSurface/ ? It ensures this as part of your testing pipeline, and requires an appropriate minor or major SemVer bump if you alter the API in a non-breaking or breaking way.
As some additional background, there's a very good reason why the F# language typechecks files sequentially. Because F# supports type inference at every scope, a single change in the body of a function can result in cascading type changes across an entire project. This kind of change can not only affect other files, but other files inside of other projects in the same solution.
A way to curb these kinds of cascading changes is explicit type annotations and/or signature files, which "lock" the type signature for a given construct/file. And the F# compiler has had optimizations in place when signature files are used to know when to re-check stuff based on this. However, these didn't extend to the sequential typechecking of files, and for the large majority of codebases that don't use signature files, these optimizations didn't really kick in anyways.