I’d call myself a down-to-earth scala engineer. By that I mean that I never say words like “covariant” unless I absolutely have to. I see programming languages as a tool to make my life as easy as possible – I chose scala because it lets me get the most output for the least input. I think this is worth stating up-front, because my initial impression of scala was that it was daunting and geared toward academics, and I admit it does have some up-front costs.
What’s cool about scala – things it makes easy
Examples
def showOff(): Unit = {
val someList = List(1,1,2,5,5,5,7,8,25,31,231,2335)
val edgeCase = List()
val uniqueCount = someList.unique.length
val secondToLast = someList.dropRight(1).lastOption
val secondToLast2 = edgeCase.dropRight(1).lastOption // None
val res = someList.search(7) // find index of 7 in logN time = Found(6)
val res2 = someList.view.map(x => x*x).search(100)
// in logN time, find where 100 stacks up against this list, while only squaring logN items = InsertionPoint(8)
val prefixSums = someList.scan(0)(_ + _) // 0, 1, 2, 4, 9...
val groups = someList.groupBy(_.toString.length) // map into a list of 1-digit nums, 2 digit nums, 3 digit nums
val lookup = someList.zipWithIndex.toMap // build a HashMap lookup of int -> index of last occurrence
val lookupFirst = someList.zipWithIndex.distinctBy(_._1).toMap // build a HashMap of int -> index of first occurrence
val countBy = someList.groupBy(identity).mapValues(_.length) // build a lookup of value -> count of occurrences
val matrix = Array.tabulate(10,10)((y, x) => y*10+x) // creates a matrix of nums 0->99
val rowIsOdd = matrix(3).forall(_ % 2 == 1) // false
val colIsOdd = (0 to 9).map(i => matrix(i)(1)).forall(_ % 2 == 1) //true
val lookup = collection.mutable.Map[Double, Double]()
def memoizedSqrt = (i) => lookup.getOrElseUpdate(i, Math.sqrt(i))
}
The above is just showing that scala has a bunch of helper functions that may seem trivial at first, but with mastery are outstanding building-blocks for all sorts of problems. Of course lodash achieves similar, for example.
Type Safety
Next the type-system. I think scala’s type system (particularly around Options and Futures) prevents a huge swath of bugs around edge-cases and concurrency by pointing out logic gaps at compile time. It takes longer to get scala code to run, but once it does it’s exponentially more likely to work correctly than node.
What I wish were changed
The gimmes
A for loop that functions like the c-style for-loop. Many linked-list and tree operations I write (i.e. leetcode) end up having to use whiles, which is blah.
An ability for a collection to have more than Int.MaxValue items. This is again something that comes up more from leetcode, but given that some collections (like Ranges and views) don’t (or at least needn’t) allocate that memory, it’s actually quite reasonable. This issue sprouts its head when you want to .search a long for example.
The culture
After the initial step of seeing map and reduce and thinking that “cool that looks handy” some of us have run into people peddling a “necessary next step” of learning exhausting thesaurus of interconnected academic buzzwords (covariant, functor, monoid…) that are really offputting. It’s been my experience that learning what a monad is has had zero effect on my productivity, and is at best a neat philosophical abstraction. Part of this complexity originates from scala’s type system. But the other half comes across as elitism and cargo-culting, fair or not.
Compilation
This is one of the real killers. I had a junior coworker who spent several hours trying to get his program to run, all while getting an illegible error. It turned out a brace was missing somewhere (or some other common mistake) which resulted in the compiler running for minutes then failing. Theoretically scala offers differential compiles, but if they are working they aren’t fast enough. Improving this alone by 75% would be more important than any and all of the fixes in scala 3.0.
Overcomplicated defaults (including libraries)
Again this is a sad one, because it would have been so easy for the normal libraries to cater toward beginners and only slowly expose complexity. I’ve seen tragic bugs (e.g. a super-fast language on a super-powerful machine misconfigured to only run 1 web-request at a time) that negate the entire value-promise of scala when put in the hands of non-experts. I’ve seen startups with hundreds of requests a day create a whole nightmare-to-debug actor-powered service because Akka. Documentation, simplicity, and guiding novices away from power-tools could have a much higher payoff than oddities like LazyList.
Also, real-world concerns (cross-future UUIDs, seeing memory usage per-thread) seem to be an afterthought, though this is true of most languages too.