Comparing Scala to F#

Datetime:2016-08-22 22:00:50          Topic: F#  Scala           Share

F# and Scala are quite similar languages from 10.000 feet view. Both are functional-first languages developed for the virtual machines where imperative languages dominate. C# for .NET and Java for JVM are still lingua franca , but alternatives are getting stronger.

My background is in .NET ecosystem, so F# was the first of the two that I started learning. At the same time, Scala seems to have more traction, largely due to successful products and frameworks like Spark, Akka and Play. That's why I decided to broaden my skill set and pick up some Scala knowledge. I've started with Functional Programming in Scala Specialization at Coursera. While following the coursera, I'm doing some notes about which language features in Scala I find interesting, or vice versa - missing compared to F#.

In no particular order, I want to share my notes in this blog post.

Implicit Parameters

A parameter of a function can be marked as implicit

def work(implicit i:Int) = print(i)

and that means you can call the function without specifying the value for this parameter and the compiler will try to figure out that value you (according to the extensive set of rules), e.g.

implicit val v = 2;
// ... somewhere below
work // prints '2'

I am not aware of any similar features in other language that I know, so I'm pretty sure I don't understand it well enough yet :) At the same time, I think implicits are very characteristic for Scala: they are a powerful tool, which can be used in many valid scenarios, or can be abused to shoot in one's feet.

Underscore In Lambdas

Underscores _ can be used to represent parameters in lambda expressions without explicitly naming them:

employees.sortBy(_.dateOfBirth)

I think that's brilliant - very short and readable. Tuple values are represented by _1 and _2 , so we can sort an array of tuples like

profitByYear.sortBy(_._1)

This looks a bit hairy and should probably be used only when the meaning is obvious. (In the example above I'm not sure if we sort by year or by profit...)

In F# underscore is used in a different sense - as "something to ignore". That makes sense, but I would love to have a shorter way of writing lambda in

empoyees |> List.sort (fun e -> e.dateOfBirth)

Any hint how?

Pattern Matching Anonymous Functions

This feature has a similar spirit: provide a way to reduce boilerplate while writing lambda functions. Instead of naming parameters and typing => x match you can just start matching immediately

val b = List(1, 2)
b map({case 1 => "one"
       case 2 => "two"})

Kudos!

Tail-Recursion Mark

Any recursive function in Scala can be marked with @tailrec annotation, which would result in compilation error if the function is not tail-recursive. This guarantees that you won't get a nasty stack overflow exception.

@tailrec 
def boom(x: Int): Int = {
  if (x == 0) 0
  else boom(x-1) + 1
}

The code above won't compile, as the recursion can't be optimized by the compiler.

The feature sounds very reasonable, although I must admit that I have never needed it in my F# code yet.

Lack of Type Inference

Slowly moving towards language design flavours, I'll start with Type Inference. Well, unfortunately Scala barely has any type inference at all. Yes, you don't have to explicitly define the types of local values or (most of the time) function return types, but that's about it.

def max (a: Int, b:Int) = if (a > b) a else b

You have to specify the types of all input parameters, and that's quite a bummer for people who are used to short type-less code of F# (or Haskell, OCaml and others, for that matter).

Type inference in F# plays another significant role: automatic type generalization. F# compiler would make types as generic as possible, based on implementation.

let max a b = if a > b then a else b

The type of the function above is 'a -> 'a -> 'a . Most people wouldn't make it generic from get-go, but compiler helps in this case.

Functional vs Object-Oriented Style

Both F# and Scala are running on top of managed object-oriented virtual machines, and at the same time both languages enable developers to write functional code. Functional programming means operating immutable data structures in pure, free of side effects operations. Without questioning all this, I find pure functional Scala code to be written in much more object-oriented style compared to F#.

Classes and objects are ubiquitous in Scala: they are in each example given in Martin Odersky's courses. Most F# examples refrain from classes unless needed. F# official guidance is to never expose non-abstract classes from F# API!

Scala is really heavy about inheritance. They even introduced multiple inheritance, traits, Stream inherits from List , and Nothing is a subtype of every other type, to be used for some covariance tricks.

Operations are usually defined as class methods instead of separate functions. For example the following Scala code

word filter (c => c.isLetter)

would filter a string to letters only. Why is isLetter defined as a method of Char ? I don't think it's essential for the type itself...

Usage of Operators

It looks like Scala culture inclines more towards the usage of different operators, not only for arithmetic operations but also for different classes from standard library and domain-specific code too. The basic ones are nice, e.g. list concatenation:

List(1, 2) ++ List(3, 4)

but others look awkward to me, e.g. stream concatenation:

Stream(1) #::: Stream(2)

Akka streams sweetness:

in ~> f1 ~> bcast ~> f2 ~> merge ~> f3 ~> out
            bcast ~> f4 ~> merge

This can go to quite an extreme, similar to what scalaz library does.

My default would be not to use operators unless you are sure that every reader is able to instantly understand what it means.

Partial Application

Not a huge difference, but F# functions are curried by default, while Scala functions aren't. Thus, in F# partial application just works, all the time

let add a b = a + b
let add3 = add 3
let sum = add3 5 // 8

Scala function

def add (a: Int, b: Int) = a + b

is not curried, but Underscore comes to the rescue

val add3 = add(3, _)
val sum = add3(5) // 8

Single-Direction Dependency

F# compiler doesn't allow circular dependencies. You can't use a function before you've defined it. Here is what Expert F# book has to say about that:

Managing dependencies and circularity is one of the most difficult and fundamental problems in good software design. The files in an F# project are presented to the F# compiler in a compilation order: constructs in the earlier files can't refer to declarations in the later files. This is a mechanism to enforce layered design, where software is carefully organized into layers, and where one layer doesn't refer to other layers in a cyclic way (...) to help you write code that is reusable and organized into components that are, where possible, independent and not combined into a "tangle" of "spaghetti code".

I think this is huge. F# forces you to structure your code in a way that avoid mutual dependencies between different functions, types and modules. This reduces the complexity and coupling, makes the developers avoid some of the design pitfalls.

There's nothing like that in Scala. You are on your own.

Conclusion

Obviously, both Scala and F# are very capable languages, and I am still picking up the basics of them. While similar in many aspects, they made several different choices along the language design trade-offs.

P.S. Overheard on Twitter:

F# isn't a bad language, it's just attached to a bad platform... The opposite of Scala actually.





About List