If we are talking about adoption and name recognition among non-engineers (despite this being a very poor way to run software engineering projects), then unless you are already a .Net shop I see no compelling reason to adopt it over JVM and (if typed FP is desired) Scala. Everyone knows Java, and Scala probably has at least twice the adoption levels of F# (pulling numbers out of thin air here).
I think there are factors to consider one over the other; just like sometimes OcAML could be a good choice as well. I wouldn't dismiss F# for production use however having seen it used successfully on large things in Windows and Linux environments.
> talking about adoption and name recognition among non-engineers
Agree that non-engineer recognition it is a poor way to run projects but sadly that matters to some engineers I've talked to as well + comfort zones. Having F#/Scala experience means they could always find a C#/Java job and that depends on the city/industry you are in as an example.
> I see no compelling reason to adopt it over JVM
That's an opinion preferring the JVM over the CLR which is a stating potentially a personal preference? Technically both are capable so I think that's a moot point - the CLR is pretty good and Java on some things is playing catchup (e.g. value types). JVM is good too but the differences won't probably mean much to most projects.
> Scala probably has at least twice the adoption levels of F#
Scala has many good points and some use cases better than F# for sure. However to play the other side some reasons I can think of on the F# side include that default web performance favors ASP.NET Core (even on Linux) from bench marking by quite a bit over say Spring Boot/Play. The HM type inference also is a plus for F# IMO (as it is for OcAML) and it leans to being more FP first giving the FP benefits quicker with less fuss. The multi threading (Futures/Async-Await) API seems more ubiquitous in the .NET space last time I checked, less LOC to use, and is exposed on a lot more libraries/clients/etc as a first class citizen. Its also IMO a lot easier to make your whole program async and has more inbuilt language support to avoid overhead. Important things in hosting an API I think especially around thread use/scalability. F# has things like value types, compile time generics, tail recursion (for FP), Spans, and other CLR features etc which allow that low level performance tuning where required - it seems to be closer to the CLR than say Scala is to the JVM. Something I do like, just like C#, is to compile it to self packaged bundles for multiple targets (ARM, MacOS, Windows, etc) in a smaller bundle than JVM based apps.
Popularity is a proxy for risk. I would assert it only matters IF the base isn't mature and it doesn't have an ecosystem to piggy back on. Popularity is a proxy measure for "something's probably missing" which for say Scala/F# isn't really the case given their ecosystems depending on your use case. OcAML is probably less popular than F# as an example but for certain use cases could still work - sadly it doesn't have that ecosystem to piggy back of and that is a factor for some teams (not all but many I've been in). F#'s been around for awhile and reasonably mature as a base.
Scala is a good language too. But I wouldn't dismiss F# in these comparisons either. There's nuances that need to be considered. Where you want a language with the crisp FP first feel/benefits of OcAML with an ecosystem just large enough to avoid risk F# is a good compromise and will work quite well. TL;DR Nothing's perfect, more similarities than differences, and as long as you aren't blocked language isn't the big factor in project success/failure that many make it out to be.
I broadly agree with your comment but want to correct some misconceptions:
- If we're going to talk about benchmarks games I'd need to see evidence, it's hard to believe .NET can offer anything more performant than say Vert.x or Micronaut :-)
- Scala has type inference (not HM, but then again almost as good)
- Futures/async/await are ubiquitous in Scala, the standard library ships with a very reasonable implementation of Future, and there is no shortage of third-party async libraries/runtimes, even leaving aside Akka which has actually been ported to Akka.NET
- Scala has value types
- No compile-time generics but powerful equivalent capabilities like type classes
- Scala has tail (self-)recusion
- JVM has plenty of low-level performance tricks
- Scala is plenty close to the JVM, I don't know how it is with F# but I know for sure e.g. things like Scala lambdas map exactly 1-1 to JVM lambdas
- Not sure when C# got 'self-packaged bundle' capabilities but if we're talking about that kind of thing, there is GraalVM and you can compile Scala apps to native binaries (as long as they don't use runtime reflection and a couple of other restrictions). E.g. https://cloud.google.com/blog/topics/developers-practitioner...
I was actually rather disappointed after looking at Scala native. From what I gathered you can’t create libraries (shared or static) only direct executables.
I wasn't referring to Scala Native, I was talking about GraalVM. But either way, compiling to native executables is a huge deal for simple deployment, even without being able to create libraries.
> I'd need to see evidence, it's hard to believe .NET can offer anything more performant than say Vert.x or Micronaut :-)
Benchmarking is interesting because it depends on your case, and the tricks used in the benchmark. Techempower has proxy benchmark at https://www.techempower.com/benchmarks/#section=data-r20&hw=... for just the web layer which shows aspcore quite high.
https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
I note it isn't as good with DB queries (DB framework) than Vert.x which gives points to Scala for db like APIs but there's tricks there too. Benchmark Game seems to have F# compare well with Java and OcAML (low level non-idiomatic code there I'm sure too). Been meaning to try Vert.x.
- Futures/async/await are ubiquitous in Scala
I need to look into this more but using them in the past it seems very library based (map/foreach/for comprehensions/flatMap/etc) whereas the .NET implementations tend to be like co-routines (state machine) that are compile time constructs with associated perf benefits. It adds a lot to performance; you want the async primitive to be as cheap especially if doing them in hot loops - .NET articles are full of Task/Async patterns and their benchmarks and optimizations to Tasks are constantly ongoing.
They seem to be extremely limiting compared to .NET types where you can compose things together (more than one value, etc). I use this for math ALL the time avoiding any GC events at all - there's a lot of cases for quick stack allocated values that can cross function boundaries. I've worked on the JVM - its possible but much harder with uglier code. .NET seems to have many more ways to avoid "boxing" than the JVM when the JIT doesn't catch it. Especially with generic methods (unless inlined). Basic tuples, ValueTasks (like futures) and such all use this construct at times.
- JVM has plenty low level performance tricks
Sure it does. I'm liking the new things in .NET Core like Span making it "safe" to do them vs Unsafe and interop. Re-using memory (slicing parts of strings without cloning chars), etc in a safe manner is a simple example but there are others.
> Scala is plenty close to the JVM
Plenty of its abstractions however however require allocations and objects (e.g. implicits everywhere) - futures being one. IMO maybe its just me but it is easier to reason about F# performance. An example in F# would be that many allocations are only allowed explicitly (e.g. math conversions) - it actively discourages implicit behaviour for simplicity I feel. e.g. Auto-Boxing in erased generics has caused perf problems in some JVM programs I used to work on. Both platforms are capable and have their own pitfalls of course.