F# versus Scala

I’m in the process of learning both.
Without starting a flame war what are the pro’s and con’s?
They feel very similiar.
But for me scala is more objected oriented.
While F# is more type driven and functional.
Feel free to elaborate.

1 Like

I think both are great languages. I have heard more praises on F# on being clean and better language semantics.

IMHO rather than language you should check the ecosystems of each language to go forward. Scala is heavy on JVM (and Scala.js), while with F# you will be inter-operating heavily with C# frameworks. A slight advantage Scala has here is that there are more native Scala first libraries (like Zio, Play, com.lihaoyi etc) than pure F# projects out there.

3 Likes

I got over “languages” (just like I got over “Linux distros”), I don’t really care anymore, they are all quite similar to me.

But they are basically COUSINS in the ML family. I think of them both as “Standard ML, plus some stuff”.

Also they are both “parasitic” languages (JVM, .NET). Did you know Scala used to have a .NET port in its early years?

That’s somewhat correct, although it’s a bit more complicated than that.

As a relatively pure ML derivative, F# is more focused on functional programming, and is optimized for that style.

Scala is intentionally broader-based (it was founded to show that you could have a hybrid of OO and FP), so it’s definitely more OO than F# is – indeed, in some ways it’s the best OO language I know.

That said, OO is as much a programming technique as a language type, and modern Scala code is less-often written in “classic OO” style that centers around complex mutable objects. Most Scala code I’ve encountered in the past ten years has been at least “soft FP”, and much of it (particularly stuff written using cats-effect or ZIO) tends to follow a pretty orthodox FP style.

So it’s more than Scala allows OO, rather than being focused on it.

Both languages are pretty type-driven, albeit with a lot of differences in how you approach that.

(Disclaimer: it’s been many years since I last touched .NET, so my F# knowledge may be way out of date.)

4 Likes

It’s your choice…

… unless you are learning it as part of some ecosystem you have to participate in.

If that’s the JVM, Graal or Javascript or Websassembly or native code, then Scala.

If it’s .NET, then F#. Maybe you can use Fable for Javascript, I’ve no idea how well supported that is.

If you’re casting around for languages, you could always go crazy and look at Kotlin and Rust. If you want to be obscure, there is Nemerle and Concurrent Clean. If you want to write papers for conferences, there is Haskell. Cough.

I spent several years doing F# and switched slowly to Scala over a couple of years - it was the tooling that gave me the initial impetus, it was pretty primitive back in 2012 for F#, and even though the now-defunct Eclipse Scala IDE was itself basic, it already outclassed the Visual Studio plugin for F#. Once IntelliJ got serious about Scala, there was no going back, and I chased after Scala contracts.

Ironically, that led me to doing Java again once I’d returned to the JVM fold.

I disagree - they’re both hybrid OO/FP languages, quite comprehensive in both aspects.

In general, Scala has more to offer in terms of concepts / features on both the OO and FP side. Whether you need these or not is up to you to find out for yourself. You’ll know when you need them. The basics are covered nicely by both languages.

The only things I recall up to in 2015 that F# had and Scala didn’t was reified generics from the .NET platform (no longer bother by this at all), some funky active pattern syntax that Scala couldn’t quite replicate (can’t remember what it was and have never felt its lack) and rather nifty ! syntax in its workflow comprehensions (I sigh and write it out in longhand in Scala for-comprehensions). There are type providers too in later versions of the language, again I’ve not felt the lack.

If you’re a glutton for punishment, you can work through all of Expert F#, Scala for the Impatient and Real World Haskell and tell us which one was the best introduction to FP. :grin: I’ve done the first and last, leaning slightly towards the first…

2 Likes

Writing a selection-sort is difficult in ocaml because it has no “variables”.
Sometimes you need one variable with mutable state.

Seems like a non-sequitur to me, but anyway, I recall a recent discussion where Essential Scala was suggested instead of Scala for the Impatient, FYI.

OCAML and F# have mutable let bindings, as well as mutable record fields via mutable and ref. So does Scala via var, but you know that already.

It’s not difficult to write selection-sort in any FP language - finding that out can be part of your journey. I suspect you are thinking of sorting ‘in-place’.

Bon voyage, schoene Fahrt, fair winds and following seas en prettige vlucht!

I’ve used both for bringing folks in. They’re both good, but very different.

Scala for the Impatient is specifically for folks coming in from Java or C#, and it’s a super-efficient introduction to the basics if you already have that context. But it tends to be a little OOish in its outlook as a result.

Essential Scala is more from the ground up, and more specifically FP-oriented; nowadays, it’s what I usually use as the first book for new teammates who need to get trained up on Scala.

1 Like

I could not found “essential scala”. Do you have a reference or link.

Use official sources: great books here, online courses here.
(I recommend “Get Programming with Scala” by Daniela Sfregola.)

Essential Scala is here, keep in mind it’s Scala 2 only.

Don’t use web searches to learn Scala, a lot of outdated / low quality stuff out there. (Except Baeldung, which is fairly good for specific tasks / reference only.)

1 Like

I did a simple fibonacci test of scala & F#.
Time for fibonacci(44)

  • Scala 6 seconds
  • F# 7 seconds

Not much difference. But I must say my F# code is shorter.
Also the vscode editor shows all types of all variables when you code. This is a big +.
For intellij-idea you must hover over a variable to see it.

In VS Code with Metals Scala plugin, there is a setting to turn on inferred types.


1 Like

I don’t understand what you’re doing. This is short enough and clear enough so I don’t see why it would be worth complaining that something else is shorter, but it would take longer than 6 seconds to run the trillions of calculations: Hang on, I messed up the complexity calculation and this is faster than 6 seconds, so now I really don’t understand what you’re doing unless you’re using a very slow computer:

def slowFib(n: Int): Double =
  if n <= 1 then n
  else slowFib(n-1) + slowFib(n-2)

This is less short but runs in nanoseconds:

def fastFib(n: Int): Double =
  var i = 1
  var a = 0.0
  var b = 1.0
  while b < n do
    val sum = a + b
    a = b
    b = sum
    j += 1
  sum

I’m not sure these sorts of speed tests are going to tell you anything at all. Both F# and Scala can have second-tier performance (like Java and C# and Go, not like Rust and C++, and also not like Python and R). If you want to learn more than the broad category into which they fall, you need to be very thoughtful about what you’re doing, how to measure each, etc…

:wave: my perspective…

F# is a wonderful language, and the primary choice between Scala and F# would actually be driven by your preferred choice of platform and ecosystem (JVM vs .NET). That being said,

Scala is actually a more “functional” language than F# because it wins in the expressiveness department; in Scala, you can work with higher-kinded types and type classes. To understand why that’s important, consider that Scala does not need .NET’s flavor of reification for generics because what it has is far more potent. These are features that Don Syme explicitly railed against because, according to him, it complicates the type system. Which is partially true; however, I’d also argue that this may be a case of sour grapes because .NET itself makes having higher-kinds complicated due to the runtime-based reification. The irony of it all is that the JVM has been an easier to target platform by languages that are not Java or C#.

To give an example of a type-class in action:

def sumList[A](list: List[A])(using Numeric[A]): A =
  val n = summon[Numeric[A]]
  list.foldLeft(n.zero)(n.plus)

I hope I’m not getting this wrong, it’s been a while since I worked with F#, but its standard library does something like this, which isn’t something you actually see in common code:

let inline sumList< ^T when ^T : (static member (+) : ^T * ^T -> ^T) 
                     and ^T : (static member Zero : ^T) > (list: ^T list) : ^T =
    List.fold (fun acc x -> acc + x) (^T.Zero) list

You can express this in the latest C#, actually, as they’ve added “abstract static methods” (which are frowned upon in F#). C# almost has type classes. Almost, not quite. But let’s go one more level; what you can’t express in neither F# nor C# is this:

import cats.Foldable
import cats.syntax.all.given

def sumAll[F[_], A](list: F[A])(using Foldable[F], Numeric[A]): A =
   val n = summon[Numeric[A]]
   list.foldLeft(n.zero)(n.plus)

// Now it works for any sequence
sumAll(Vector(1,2,3))
sumAll(List(1,2,3))
sumAll(Array(1,2,3))

This difference is not only academic, manifesting itself for example in F#'s AsyncSeq, which is a library combining Async with Seq. Which is nice and all, but in Scala, all the features in this AsyncSeq are expressed more generically in the Typelevel Cats library, increasing reuse for other types. To make a comparisson, it’s like in Go, when people needing generics were simply duplicating the code for all the types they care about. Which arguably, works for some common cases, but also sucks.

F# does have Hindley-Milner type inference, however, it only works as long as OOP subtyping isn’t involved, and due to its .NET interop and dependence, it has a lot of OOP subtyping. Hindley-Milner in general makes error messages hard to read and understand, although, granted, this is more relevant in languages with more advanced type systems. Even in languages with HM (e.g., Haskell), the general advice is for public functions to have explicit types; otherwise the contract exposed is fragile. And also, the more advanced the type system, the more difficult HM becomes. For example, the more advanced features of Haskell you use, the more HM breaks down. See also Idris, in which type inference is undecidable in general.

I always felt that this is a general problem with F#, trying to combine two separate worlds. For example, its generic types can have restrictions that can’t be expressed in .NET (like in the sumList above). Therefore, to use them, you can only use inline functions, which are functions that are not seen by the .NET runtime, so they can’t be passed around as values. F# has a sort of duality to it, not trying too much to mix the functional aspects with OOP, like Scala or OCaml are trying. This can be considered a feature, except, one interacts with .NET and OOP a lot in F#, more than in your typical Scala FP project, actually. To give one example, when I last tried, in F# serialization was still done using runtime reflection, whereas in Scala we have libraries like Circe which are working entirely at compile-time, with static safety as well.

Back to the FP aspects, Scala has a very healthy ecosystem of FP libraries. FP has been so successful in Scala that it ended up with two mature and competing ecosystems for it, see Typelevel and ZIO. Scala also has one of the best books on FP around: FP in Scala.

Note that there are aspects of F# that I like. Off the top of my head, I can think of:

  • Type providers — you could build something similar in Scala in a library, actually, however it’s challenging, and I haven’t seen something polished yet;
  • Computation expressions — Scala has “for comprehensions”, but F#'s computation expressions are more evolved; this is for working with “monadic” types, but note that Scala also has plans to go the way of Kotlin and provide baked-in support for continuations (thus providing an alternative to monads because it was too boring before :stuck_out_tongue_winking_eye:);
  • LINQ support;
  • Active patterns (although Scala’s pattern matching is quite nice, too).

F# also has features that Scala can do better, but that are nice to have regardless, such as “units of measure” or “code quotations”.

F# resembles Python, in the sense that its designers have introduced features to solve concrete use-cases. But, just like in Python, it has features that don’t feel orthogonal or very generic; in Python, for instance, I can think of several features that were added to avoid adding multi-line lambdas. So much for the “one way of doing things”.

But overall, F# is a language I would enjoy, if I wanted to target .NET. However, independently of .NET, I’d choose Scala, as .NET can be … a learned taste.

I’ve always preferred the Java ecosystem because it has always been closer in spirit and culture to Linux and Open-Source. This has pros and cons. dotNET feels more like a cathedral and Java feels more like a bazaar. I like having competition and choices, instead of having Microsoft impose its solution du jour.

The JVM also has had a great evolution, in some ways leapfrogging .NET (e.g., runtime optimizations, the “pauseless” GC implementations Project Loom, GraalVM), and in some ways closing the gaps (upcoming Valhalla, which finally brings value types and maybe generics specialization).

But I’m sure that people can find reasons to love the .NET ecosystem as well, since it has evolved a lot, too.

I hope this helps.

10 Likes

This version is just as fast, and doesn’t need any variables:

def fastFib(n: Int): BigInt =
    def loop(i: Int, a: BigInt, b: BigInt): BigInt =
        if i > 0 then loop(i - 1, b, a + b)
        else a
    loop(n, 0, 1)

You can take any for/while-loop and turn it into a tail-recursion in which all the variables become function parameters :wink:

Whether that’s better or worse than a for/while-loop with variables, for me, it depends on the use-case. More often than not, I like the pure version better because it forces you to have explicit branches and evolution of state. But every so often I also go for the mutable version.

1 Like

I wanted to test the speed of the stack , so i’ve written it as slow as possible.

Of course! But the while loop’s single-pass nature is even more obvious, which is why I wrote it that way.

1 Like

Do you prefer vscode or intellij-idea as editor ? Which is best and why ?

IntelliJ IDEA is the best, and the Scala plugin is free with the Community edition.

I use both, as VS Code is lighter. One thing that bothers me about IDEA is the long wait times when it has to refresh the build config, which happens most often when I switch branches. I’ll fire up VS Code for quick edits, especially of the build config.

I have Vim shortcuts configured in both, though, so it’s not really impacting my workflow much. You can use Vim or Emacs as well, as Scala’s Metals is integrated with them, too. I use the LazyVim distribution, if you want to play, although learning Vim and a new programming language might be too much.

The significant difference is that VS Code with Metals uses the official compiler frontend, meaning that it will give you the same error messages that the Scala compiler gives. Jetbrains tend to implement their frontends because they want instant feedback and partial compilation when people type stuff in the editor. The experience is overall better, but the downside is that sometimes it doesn’t agree with the official compiler, so you could have Scalac give you some errors that IDEA doesn’t see or vice versa. So people that are on the cutting edge of Scala’s type system tend to prefer Metals.

IntelliJ IDEA is a full-featured IDE, though. Refactoring, code completion and navigation work great, usually, best in class.

1 Like

What about “formatting” ?