a) useful utilities for actually writing applications
b) decent documentation. Not just pages of type signatures, but demonstrations of the libs usage.
Everything you need is here: JSON, config, units of measure, streams, testing, validation, a really nice JDBC wrapper (https://tpolecat.github.io/doobie/)
Throw in https://http4s.org/ and you've got yourself a rock-solid, purely functional stack with sensible, documented APIs, a more readable syntax and better tooling support.
I urge anyone who learnt some Haskell, thought "man this shit sucks, I'm never going to write something useful in this" to at least give FP Scala a chance. Here's a useful service template to start hacking with: https://github.com/http4s/http4s.g8.
I feel conflicted about this having programmed in both OO-heavy and pure FP Scala. On the one hand, sure if you want to write pure FP in Scala, some of the tooling is better than Haskell. Most notably the IDE situation with IntelliJ's Scala plugin has made leaps and bounds in progress the last few years and mostly handles pure FP Scala code just fine. And having access to the JVM ecosystem is an absolute god-send and huge boost to productivity. This is true even outside of library dependencies when coding. If you try deploying to production with Haskell, there's often a large gap in production tooling (monitoring, diagnostics, GC tuning, etc.) when compared to the JVM.
On the other hand Scala has its own share of infelicities when it comes to pure FP. I've mentioned this elsewhere, but the core language of Scala, that is the language that is left after you desugar everything, is OO and the FP part is really just a lot of syntax sugar mixed in with standard library data structure choices. That means if you're coming from a pure FP background a lot of things will seem like hacks (methods vs functions/automatic-eta-expansion, comparatively weak type inference, the final yield of a for-comprehension, subtyping to influence prioritization of implicits for typeclasses, monomorphic-only values vs. polymorphic classes, etc.). Treating Scala as an OO language side steps a lot of these warts.
And then there's the social factors; the Scala community is split on how much it embraces pure FP and pure FP is a (significant) minority within the community. This carries over to the library ecosystem where things are basically split into Typelevel/Typelevel-using and non-Typelevel libraries. Many workplaces have a fear (well-placed and not so well-placed) of the Typelevel ecosystem. Years ago Scalaz was something akin to the bogeyman in some places. Cats has a bit of a softer image, but still comes off as an "advanced" library in the Scala ecosystem.
Most of the social weight I feel is behind the non-pure-FP parts of Scala. Sure some of the libraries in pure FP Scala have good documentation (but I don't actually think the situation here is far better than Haskell's once you leave the core Typelevel libraries). The ones with excellent documentation though live outside the land of pure FP (Akka, Play, Spark, Slick, etc.).
You know, I really agree with a lot of this. It's maybe not an unreasonable argument to make that maybe OO-heavy or Akka-ish Scala might be a better investment for a lot of people, considering the weight and momentum of the communities maintaining these, and these expressiveness of the language in these domains.
But I think if we're only talking about pure FP, or at least something close to very pure, I think you're still getting a better deal than Haskell, even in spite of all those quite legitimate downsides you mentioned. My own personal biases mean that I will always prefer pure FP to anything else (I personally didn't love Akka and Play when I used them briefly), but that's an argument for another day.
Perhaps much of what you describe can be attributed to Scala being a multi-paradigm language. As with other programming languages of this nature, supporting multiple paradigms can be both a strength and weakness, depending on those whom use it and their expectations.
Whether this is right/correct or wrong/incorrect is left as an exercise for the reader ;-).
It's not really "equally" multi-paradigm though. All of Scala's FP parts can be desugared into OO. The reverse is not true. This has persisted in Dotty, where proposals to e.g. make typeclasses an atomic entity have been superseded by proposals to continue to encode typeclasses with separate mechanisms. That's the annoying part when doing pure FP in Scala. You can sometimes feel like you're fighting against the grain in a way that isn't true when on the OO side.
> It's not really "equally" multi-paradigm though.
True. Most multi-paradigm languages are not equal in each paradigm with which they support.
> All of Scala's FP parts can be desugared into OO. The reverse is not true.
This is a bit of a strawman argument, as any functional programming environment can be implemented by, or "desugared into", an object system. Contrast this with the fact that mutable OOP systems cannot guarantee Referential Transparency[0] and your second assertion is proven.
However, this simply proves that Scala supports more than one programming style. Whether a given code base employs FP, OOP, imperative programming, or some mixture therein, is a decision left for the system authors and not the language. It is left as an exercise for the reader to determine if that is good or bad.
> This has persisted in Dotty ...
AFAIK, Dotty is intended to be a new experimental language. I do not follow its development nor progress.
> That's the annoying part when doing pure FP in Scala. You can sometimes feel like you're fighting against the grain in a way that isn't true when on the OO side.
I respect what you identify but do not agree with your annoyance. But that's just me.
I don't think it's a strawman. Every language that has syntax sugar gets split by the community into the "core" language and the "sugar" on top. This is a very different comment than saying every programming paradigm can be implemented in terms of another. There are examples of languages where OO is a syntax sugar layer and FP is the core abstraction that OO desugars into. There are not many since for a variety of reasons people do not like doing this, but there's some.
A more fleshed out version is O'Haskell (which unfortunately died out in the early 2000s).
Mutability can be built (or more accurately faked) on top of referential transparency as well through syntax sugar in a very similar fashion to how Scala builds FP on top of OO. Indeed this was the original impetus behind do-notation in Haskell, but it stopped short of trying to make do-notation look like ordinary Haskell code. If you had syntax sugar that elided the difference between do notation and ordinary equality and unified IO with normal types then you'd have mutability in your language implemented through syntax sugar on top of an immutable core language (you could call it automatic IO expansion to be cheeky that automatically inserts a call to pure in front of any non-IO code used in an IO context). In fact I could see a rather reasonable case to be made for such a construct.
Scala similarly "fakes" (not necessarily a bad thing!) a lot of stuff. This is how automatic eta expansion and special function instantiation syntax (the arrow as opposed to new Function1(...)) elide the difference between methods and functions in Scala and let the language pretend e.g. that methods are first-class entities that you can pass to another method (which is not true, they must be wrapped in a class first just like Java) and let you pretend that methods have the same type signatures as functions (when in fact methods have a special method type that can be polymorphic whereas function are always monomorphic. In fact you cannot write the true type of a method in a first-class way in Scala; it is a special type that exists outside of the normal type hierarchy that is referred to as a "method type" in the Scala spec). This is leaving aside the encodings of typeclasses, ADTs, fully polymorphic functions (FunctionK in cats), etc.
In all these examples it is the FP concept that is "faked" (higher-order methods and polymorphic functions respectively in the two examples) and the OO concept (method taking an ordinary instance of a class and generics in methods) which is fundamental.
Dotty is explicitly blessed as Scala 3. I would highly recommend keeping high-level tabs on it if you're a Scala programmer. You don't need to know the specific details of it, but note that Scala 2.14 will be built specifically with Dotty in mind. It is the future of Scala (https://www.scala-lang.org/blog/2018/04/19/scala-3.html). And it comes with a lot of goodies! I'm really excited about it. More importantly for this discussion the current encoding of typeclasses in Scala 2 still desugars to implicits + OO classes instead of the other way around (which is perfectly possible where typeclasses are the core abstraction and implicits and OO classes are built using typeclasses).
Another great resource for doing FP in Scala is "Functional Programming in Scala"[0]. It's a very well written book and goes far in introducing key FP concepts IMHO.
The pure FP Scala community understands your complaints more than the Haskell community.
https://typelevel.org/ is chock full of
a) useful utilities for actually writing applications
b) decent documentation. Not just pages of type signatures, but demonstrations of the libs usage.
Everything you need is here: JSON, config, units of measure, streams, testing, validation, a really nice JDBC wrapper (https://tpolecat.github.io/doobie/)
Throw in https://http4s.org/ and you've got yourself a rock-solid, purely functional stack with sensible, documented APIs, a more readable syntax and better tooling support.
I urge anyone who learnt some Haskell, thought "man this shit sucks, I'm never going to write something useful in this" to at least give FP Scala a chance. Here's a useful service template to start hacking with: https://github.com/http4s/http4s.g8.