It baffles me that there are languages with non-profit foundations and are financially backed by multiple corporations which still have bad build systems. It is the most important investment you can make into a programming language.
It baffles me that there are languages with non-profit foundations and are financially backed by multiple corporations which still have bad build systems. It is the most important investment you can make into a programming language.
> I'm completely convinced that F# (along with Scala, Haskell, and OCaml) adoption has stalled due to having ridiculously bad build systems.
Scala? I am not trolling here: Are you joking? Scala is part of the Java ecosystem. Sure, Maven gets lots of hate on HN, but it is very mature and has excellent integration with IDEs and CI/CD systems (TeamCity, Jenkins, etc.). In the last 10 years, many Java developers have moved to Gradle, which has equally good integration. > Hell, 80% of the reason I choose Rust over C++ for embedded work is because of the build system.
What is wrong with CMake for C++?My guess is that it's much longer than for the equivalent Java apps.
> What is wrong with CMake for C++?
It doesn't manage dependencies.
Possibly `eval $(opam env)` is something that should just go in your ~/.zshrc
The OCaml folks have done some work recently to improve the onboarding documentation, which I think is going in a positive direction
e.g. https://ocaml.org/docs/installing-ocaml (the eval as a one-off post install command)
And then guiding people to use 'switches' https://ocaml.org/docs/opam-switch-introduction, which I totally missed when I started with the language.
> Local switches are automatically selected based on the current working directory.
At the same time, a lot of the cool features (list comprehension, pattern matching, immutable records) have slowly trickled into c#, giving even less incentive to switch
C++: invoke compiler for all compilation units, invoke linker to combine object files into executable
C#: invoke compiler once with all source files listed
F#: same as C# AFAIK, except file order matters!
Secondly, often very differently shaped requirements. The dotnet SDK tries to keep its build specification (.csproj) files editable by Visual Studio, which is why most of the stuff in them is just XML properties.
You probably could build C#/F# with Bazel but that's not what Microsoft chose, and you kind of need to stay aligned with them and the large amount of MSBuild files in the SDK.
Maven is awful. SBT is awful. Gradle is awful. I've used them all professionally, and the best I can say about them is that you can get the job done with them.
Newer languages and newer build systems are much better experiences, because of decades more hindsight and because language designers think about tooling from the start. Java was designed with the assumption that all software projects were built with Make, and with no ambition to improve on that. There was no Java-specific build tool until Ant was released as a standalone tool circa 2000.
> What is wrong with CMake for C++?
Granted, most of what's wrong with CMake is the problem it solves. Probably there's no solution that wouldn't be at least close to as awful as CMake. But it is objectively a hideous experience compared to any language created in the last 15 years.
C# has a multi-pass compiler so that it can compile and link the components from multiple files, without need of placeholder declarations, regardless of the order the symbols appear in the files.
F# has a single pass compiler, which keeps the compiler implementation simpler, but the file, and symbol definition order does matter that way. This is totally intentional, this is supposed to make the codebase more straightforward, with which I personally agree with. This avoids the need for declarations and centralization of them, the includes all the baggage that comes with that approach, and all the complexity C# has. I have rarely found a limiting factor, though there are some cases when it can be a bit inconvenient, for me the application setup/composition (~DI, but I prefer more static approach in F#) needed some cumbersome refactoring in some cases (have only vague memories by now, and yes, I know co-recursive types exists)
I really like F#, but rarely have to opportunity to work in it.
On the other hand many of these features are really convenient and handy in F#. Adding many of the oh-my-gamedev-such-speed features from C# to F# also makes its syntax less pleasant to work with.
Personally I also think that the C# async model is terrible, and was a grave mistake. The F# async model with computation expressions, and explicit control over their execution context was a better approach, and I'm really sorry the hack-something-together to unblock the event loop WPF/frontend-dev usecase won over the more disciplined backend-focused approach.
You may not remember the early .net core times with yeoman and other then-current javascript ecosystem originated things applied in really cumbersome, half-assed ways, with lacking docs and always being in flux for years. The project.json era was terrible.
Also msbuild was way worse 10-15 years ago...
Mono with automake was special circle of hell IMO, I have very small exposure but it was really unproductive and painful.
public record Name(string First, string? Last = null);
public record Register(Name[] Members);
...
var register = new Register([new("John", "Doe"), new("Neo")])
It probably depends on what you're writing. I'm not using async much at all so I don't feel the pain of it.a) it poisons all interfaces it touches (common trait of async in other languages as well)
b) C# async Task -s typically are created in Running state without any easy control over when, where and how they will execute. Controlling these things is far from trivial, and and requires lot of extra effort.
In F# the traditional async block is a builder for an async workflow, and you could then submit this workflow to an executor that is easy to configure for the execute model best suites you, eg. thread pool, single thread with continuations, maximum number of "operations" in flight, etc. The fact that it is not started right away also makes it easy to create your own executors.
Having to deal with backpressure in C# style async is way harder IMO. On the other hand when writing a UI app, always having to submit to an executor might seem inconvenient, and you generally don't have to handle thousands of concurrent requests all reaching out to a (different) backend and avoid DOS-ing it. This is why I wrote that this way made with a frontend-centric approach in my opinion.