An Unbiased Comparison of F# and Scala
Given my history as a .NET developer I learned Functional Programming via F#, but I just started a new job as a Scala developer. Naturally, I’ve been comparing the two languages and the quirks and nuances that make could make them enjoyable or problematic. To summarize quickly, I think Scala is more approachable but less “pure” than F#. Scala seems to have a diverse set of influences whereas F# tries to stick closely to proven Functional Programming basics.
Functional but Object Oriented
Both Scala and F# claim to be primarily functional languages but are also fully object oriented. While F# is essentially OCaml.NET and Clojure is basically Lisp for the JVM, Scala is a completely new invention. Scala also strikes me as more object oriented than F#.
For instance, Scala includes both mixins and monkey patching. On the other hand, F# only has monkey patching. Both concepts I learned from Ruby and I associate with pretentious arguments about “which is more OO”. With that said, I love the fact that Scala has mixins. It’s a much cleaner dependency injection technique than IoC containers (which is how we did it in C#).
Functions
Given F#’s OCaml ancestory, it tends to define methods in an ML-like way. For
example, an add
function in F#:
In the spirit of OCaml, this has a signature that looks something like
which means, “a function that takes int
and returns a function that takes
an int
and returns an int
”. This plays perfectly into function currying and
partial function application where you might apply one argument at a time:
Scala also has currying & partial function application, but it’s less structured. While F# functions are curried by default and ready for partial function application, Scala functions aren’t but can easily be curried on demand:
Most of the time you don’t need function currying, so I like that Scala makes functions more familiar. But at the same time, currying isn’t hard in Scala, since there’s a native syntax for applying only some arguments via a pick-n-choose templating style.
F# Is Stricter FP
F#’s ML-style of function definitions that are curried by default makes for a
more pure functional style. In F#, partial function application is used everywhere,
so when doing List
operations these functions are implemented in separate
modules and “pipelined” using the |>
operator:
Result:
On the other hand, Scala implements these methods as traits that are “mixed into”
List
. Here’s the same example in Scala:
I like to say that this means F# is more “pure” functional programming. I say this mainly because Scala chooses to use methods instead of plain functions in cases like this. I’m not sure if this actually makes F# “better”, but it is notable.
Discriminated Unions vs. Case Classes
This is a very powerful concept in both languages. You can’t say you’ve mastered either language until you’ve learned how to use them effectively. However, they’re not equal concepts. Here’s a quick overview:
And the equivalent Scala code:
The first point to contrast is that scala case classes are just a class hierarchy, whereas F# unions appear more like C enums but with different “shape”. In reality, F# unions are actually implemented as a class hierarchy, like Scala.
In F#, all known values of the union must be declared in one place. However, Scala’s
class hierarchy approach means that you could define more values in other files or
JARs. This is the default behavior, but I included the sealed
keyword which limits
definitions to the same file.
This seems like a bad default behavior to have. If the compiler doesn’t know all
possible values of a union, how can it determine correctness in a match
statement?
There’s definitely some loss of type safety there, but it is only a default, so
I shouldn’t complain too much.
Beyond that issue, there is F#’s concept of record types. They’re immutable classes that can’t be inherited and have special semantics for copying:
Scala doesn’t seem to have a record type concept. Instead, case classes are reused
for the same purpose. All case classes automatically get a copy
method mixed in:
I’m still undecided on whether I like how Scala merges the concepts. On one level, it’s simpler since there appears to be less concepts to learn. But on another level, the semantics are broken - if you want a record type you have to define a “case class” which infers that you’d normally use it like an enum.
Conclusion
Scala is a more approachable language than F# but F# has a stronger sense of type safety. F# also has a much stronger type inference system, which leads to less type annotations. Regardless, I think Scala will recieve a much broader uptake given that it has a much more familiar syntax to C/C++/Java/C# developers. On some level, I like to think of Scala as being more of “a better C#” than “like F#”. Each will have it’s uses, but I think Scala will go far because of that.