Multi-threading can often be handed off to the OS in the form of just run more processes. So in most cases there is really no need for the language to handle it.
Its still nice to have shared memory especially in a functional language where due to lots more immutability it isn't as big of a price (i.e. more concurrency safe). Especially if your sharing in-memory caches and the like.
I've seen this save tons of dollars in cloud setups (100,000's) in my career in more than one place. Safe multi-threading was marketed as part of the advantage of functional programming to many people; which IMO why I find it strange that OcAML didn't have it for so long. Lightweight threads (e.g. Tasks, Channels, etc) use less system resources than processes as well.
You can do anything with anything but you usually pay some price of inefficiency to do so. That may be small, but sometimes it is large enough to matter.
Agreed; especially since it didn't have it originally. I'm sure some compromises were made to do it in a way that fits into the execution model/doesn't cause regressions to older code. They are both good languages for sure which makes sense because one is derived from the other.
F# through itself and .NET has had the equivalent functionality for many years however (Threads, Tasks, Async's, Channel libraries, etc) being the one of the first languages (before C#) to have an Async construct. I would imagine it would take some time I think for OcAML's ecosystem to catch up API wise with this where it matters for application development. Looking at OcAML's recent release notes I see work to slowly migrate multicore into OcAML but the feature itself hasn't landed yet? Think it comes in 5.0.
I have to say though F# performance is quite good too those days from working/dabbling with both languages + the ecosystem is bigger. I would like to see/understand where the OcAML perf advantage is there is any for evaluation. The CLR has really good performance these days; its has features to optimise some things (e.g. value types make a big difference to some data structures, hot loops vs the JVM) from my experience especially if you don't allow Unsafe code for many algo's/data structures. For example I just looked at the benchmarks game (I know has its problems including bad contributed implementations for niche ecosystems) but it shows .NET Core (and therefore F#) performance is within the same scale as OcAML at times beating it (https://benchmarksgame-team.pages.debian.net/benchmarksgame/...).
If you don't need to share any data between processes, sure. Otherwise, you will find yourself forced into a pattern that is:
* extremely costly
* hard to make portable cross-platform
* hard to make secure (since data external to the process can't be trusted like shared-memory data can).
* requires constantly validating, serializing, and deserializing data, wasting developer time on something irrelevant to their problem domain
* adds a bunch of new failure modes due to the aforementioned plus having to worry about independent processes crashing
* fails to integrate into the type system of your language (relevant for something like Rust that can track things like uniqueness / immutability for you)
* cannot handle proper sharing of non-memory resources, except on a finicky case-by-case basis that is even more limited and hard to make cross-platform than the original communication (so now you need to stream events to specific "main" processes etc., which is a giant headache).
* Can cause significant additional performance problems due to things like extra context switching, scheduler stupidity, etc.
* Can result in huge memory bloat due to needing redundant copies of data structures and resources, even if they're not actually supposed to be different per thread.
Make no mistake here, when you're not just running multiple copies of something that don't communicate with each other, "just use processes" is a massive pain in the ass and performance and productivity loss for the developers using it, as well as hurting user experience with things like memory bloat. The only reason to go multiprocess when you have threads is in order to create a security boundary for defense in depth, like the Chromium does--and even they are starting to reach the limits of how far you can push the multiprocess model before things become unusably slow / unworkable for the developers.
There isn't really a need for anything, which is kind of the point. You can use a random language that's missing a ton of functionality and you can probably make it work.