Seven Languages: Week 4 (Scala) - Day 2
Day 2 of Scala focused on its functional programming prowess. Since this is the first time functional programming has been introduced in this book only the basics were talked about this chapter, and I’m already pretty familiar with those so this was an easy chapter for me.
The exercises were a lot shorter than the previous day’s, although me going overboard last time probably had something to do with that! Today the main exercise was a small task that makes sure you know how to use Traits, Maps, and read files.
My familiarity with the topics in this chapter and the shorter exercises both mean this will be a shorter post than last time.
(This article is part of a series of posts I am doing about my journey through the exercises of the book Seven Languages In Seven Weeks. The article previous to this one is Week 4 (Scala) - Day 1. For an overview see the Seven Languages project page.)
Topics Covered
Topics in Day 2 included functional programming, immutability, higher order functions, collections, and the representation of empty values (Null, null, Nothing, Nil, None and Unit).
Functional Programming
Scala has thorough support for functional programming. It has closures, convenient syntax for anonymous functions, currying, a focus on immutability, pattern-matching, algebraic data types, type classes, and it has many higher-order functions defined for its collections.
A somewhat lengthy but very interesting read on this topic is On Scala, Functional Programming and Type-Classes by Alexandru Nedelcu.
Collections
Scala has great collections. Scala doesn’t do anything revolutionary with collections. These statements are both true and it’s not a bad thing! If anything it’s a reflection of the growing number of great collections libraries in modern languages. What it does mean is that it is harder for me to point to one or two features and say “Here, you see? That is why this is so good.”
Scala’s collections have obviously had a lot of effort and attention to detail put into them. You’ve got good default implementations of the basics (sequences, sets, maps) and many implementations of each kind that have different performance characteristics, all of which are nicely documented. There are lots of useful helper functions defined at appropriate levels in the well-organized class hierarchy. Functional programmers will be happy to know that any String is also an IndexedSeq!
Here’s what the abstract part of the collections class hierarchy looks like:
A ton of general useful collection methods are defined high up on Traversable and Iterable so that all can use them. It makes using them very consistent and convenient.
One of these that is worth pointing out is par
, which lets you turn sequential operations on collections into parallel ones with a single word. There are a growing number of languages that have this capability now: Haskell, C#, F#, Scala, and soon Java 8 (I have undoubtedly missed a few, please let me know if another comes to mind).
Null, null, Nothing, Nil, None and Unit in Scala
This book tried to explain this, but the explanation only served to confuse me further. I read a few other explanations online and between them pieced together the higher-level information I wanted to know.
This resulted in me making a reference post that gives a Cliff’s Notes version of emptiness in Scala. Null and Nothing basically only exist as support for the other values, and Unit isn’t as easy to confuse with the others. That leaves null, Nil and None which all have similar names and represent “empty” in some way. Fortunately, all three of these concepts are fairly distinct and even have direct analogues in other languages.
- null - Exactly the same as
null
in other languages. - Nil - Empty list.
[]
in other languages, same asList()
in Scala. - None - The empty case of Option, also called
Nothing
in Haskell.
If you want more information, see my full post about this, there are links to more detailed information in there as well.
Highlights From Exercises
The exercises this week were a lot simpler than my jumped up Tic Tac Toe from last time. On one hand, it was nice because it took less time to finish. On the other… I have less to write about now!
So let’s evaluate this method I wrote and see if we can make it better:
def censorThyselfVillain (suspectPhrase : String) : String = {
var wholesomePhrase = suspectPhrase
for ((key, value) <- curseWords) {
wholesomePhrase = wholesomePhrase.replaceAll(key, value)
}
return wholesomePhrase
}
Its purpose is to replace ‘offensive’ words with a chosen set of ‘less offensive’ ones. As far as imperative solutions go I’d say it is pretty easy to read - it’s straightforward and not unnecessarily wordy.
But Scala is a hybrid language - it’s capable of functional programming too, not just OO or imperative. Does that help in this particular case? Let’s see:
def censorThyselfVillain2 (suspectPhrase : String) : String = {
curseWords.foldLeft(suspectPhrase)((acc, pair) => acc.replaceAll(pair._1, pair._2))
}
This function can be reduced to a simple fold. As easy to understand as the previous example was, this one (provided you’re already familiar with fold) is even nicer. It is good to have options.
Full Solutions
Here is a nicely formatted version of my solutions to the exercises from Day 2 of Scala. The home of the following code is on github with the other exercises.
Find:
I'm not entirely clear what this question is looking for. Maybe something like this?
I'm not sure I've got my terminology straight here, but I will give it a go. In Scala, a code block is a piece of syntax that creates closures.
Do:
var stringList = List("here", "is", "a", "list", "of", "strings")
var totalStringSize = stringList.foldLeft(0)((total, string) => total + string.length)
var totalStringSize2 = (0 /: stringList) {(total, string) => total + string.length }
println(totalStringSize)
println(totalStringSize2)
import scala.collection.mutable.HashMap
trait Censor {
var curseWords = Map(
"(?i)Shoot" -> "Pucky",
"(?i)Darn" -> "Beans"
)
def setCurseWords(newCurseWords : Map[String, String]) = {
curseWords = newCurseWords
}
def censorThyselfVillain (suspectPhrase : String) : String = {
var wholesomePhrase = suspectPhrase
for ((key, value) <- curseWords) {
wholesomePhrase = wholesomePhrase.replaceAll(key, value)
}
return wholesomePhrase
}
def censorThyselfVillain2 (suspectPhrase : String) : String = {
curseWords.foldLeft(suspectPhrase)((acc, pair) => acc.replaceAll(pair._1, pair._2))
}
}
class PolitenessEnforcer extends Censor
var enforcer = new PolitenessEnforcer()
val rudePhrase = "Shoot, my darn head is stuck in the dumb freaking door. Holy gosh Batman!"
println("Spoken frankly: ")
println(rudePhrase)
println("The enforcer menaces with spikes of brass: ")
println(enforcer.censorThyselfVillain(rudePhrase))
import scala.io.Source._
var cursesFromFile = Map.empty[String, String]
fromFile("curseWords.txt").getLines.foreach { line =>
val curseWordPair = line.split('|')
if(curseWordPair.length == 2) {
cursesFromFile += ("(?i)" + curseWordPair(0).trim) -> curseWordPair(1).trim
}
}
val stricterEnforcer = new PolitenessEnforcer()
stricterEnforcer.setCurseWords(cursesFromFile)
println("The enforcer menaces with spikes of onyx: ")
println(stricterEnforcer.censorThyselfVillain(rudePhrase))
Next in this series: Day 3 of Scala (coming soon).