Quickly learning the basics of Scala through Structure and Interpretation of Computer Programs examples (Part 1)
Why Scala is so exciting
I’ve used a lot of programming languages over the last 15 years as an academic researcher, data scientist, and engineer. I’ve recently learned Scala and have found it to be an exceptionally elegant and powerful language. Specifically:
- Scala is a highly legible language that is a pleasure to read.
- Scala code is concise.
- Scala has a powerful type system to catch errors while you’re writing code. The type annotations further add to the legibility in that you know the types of variables for each snippet of code.
- Scala encourages a functional programming style that is more natural to many non-engineers than conventional procedural programming languages.
You can find more details of my thoughts on Scala in my article, “Initial impressions of Scala from a Java and Python data engineer”. Also, you can check out scala-lang.org for an overview of what makes the language great.
In this article, you’ll explore some basic Scala examples so you can see for yourself how simple and elegant the language can be. Yes, Scala is a rich language and ecosystem that has a steep learning curve for people looking to fully master the technology. But I believe anyone can start using Scala with just a basic understanding of the language. One can always learn more advanced Scala features later if they need a more sophisticated language.
You may even appreciate using such a highly scalable language (which is where the name Scala comes from) in that there are always more advanced features to learn when you have a problem appropriate for them. There are even a lot of Open Source libraries that extend the language to make it more powerful in certain domains such as analyzing and processing “big data”.
In this article, we’ll work through some basic programming examples from the legendary “Structure and Interpretation of Computer Programs” (SICP) book. You can run these examples, including modified versions, in your browser using Scastie. We won’t digress on the details of any single example and instead, we’ll explore a range of examples so you can start to see the patterns for yourself. Small exercises are included to help you learn Scala. We’ll again use Scastie for testing out the code we write in exercises.
Before diving into SICP, let’s cover the time-honored tradition of showing a Hello World program, which simply prints “Hello World” as output.
We’re simply using the “print line” function. You can copy this code into Scastie to test this out.
SICP starts by considering some example math expressions.
Note that the lines starting with
> are not Scala code. I just added those lines to show us the results of each expression.
As an exercise, how would you multiply the numbers 7, 3, and 5 in Scala? Try out your code in your browser using Scastie.
In Scala, we can create a variable and assign it a value as follows.
Variables can be reassigned.
Variables are useful because they allow us to save the results of computation for reuse. They’re particularly helpful when we want to use the value of an expression in multiple places.
Vals (i.e., constant variables)
In general, Scala encourages us to avoid reassigning variables as that can make programs more complicated. Instead, Scala encourages the use of
val variables, which cannot be reassigned.
We should use
val in place of
var whenever possible to keep our code simple and easy to reason about. Working with Scala will teach us patterns to avoid variable reassignment.
We’ll commonly want to define computation that can be reused. For that, we use functions. Functions take input parameters and compute a resulting value. E.g.,
The big thing to note in the function definition (i.e.,
def) is that we have to specify the types for both function parameters and the return value of the function. In this case, the function takes a single integer parameter,
x, and returns an integer.
Functions can take more than one input parameter.
In the previous two examples, the function just consisted of a single expression. We can have more complicated functions with multiple statements, including defining local variables.
Brackets are used to group together these multiple statements. Note, that the function evaluates to the value of the last expression and there’s no need for an explicit
return statement as in some other programming languages. This example also introduces a new type,
Double, that is used to represent numbers with decimal points; in math terms, real numbers.
Function parameters don’t have to be limited to numeric types and can take any data type. For instance, there is also the
String type, which consists of text; i.e., a string of characters. Further, functions don’t have to evaluate to a usable value and these functions are declared with
Unit return types.
How would you write a function that takes two integers, defines a
val that is the sum of the two integers, and then print that value? Here’s a template.
Does your code run in Scastie?
if expressions to conditionally evaluate expressions. Unlike more procedural languages where
if is a statement (i.e., code that doesn’t evaluate to a value), the Scala
if is an expression that evaluates to a value.
Just like functions, you can have a conditional expression that combines multiple statements (e.g., assigning a
val) and expressions. Same as functions, brackets are used to create such a compound expression and it evaluates to the value of the last expression.
if expressions can be used to conditionally execute statements with side effects; e.g., printing a value. Note, a side effect is anything that happens outside of our code evaluating to a value, including printing output.
In this example, there is no else clause.
Can you modify the function to be called
printClassification that prints both large and small numbers, with a suffix that says either “is large enough” or “is too small”?
Conditionals can be nested as shown in the following example for SICP
abs function for computing the absolute value of an integer.
Sometimes we just want to repeat something in code. For example, printing something out n times or summing the integers from a to b. Historically, many programming languages used something called loops to repeat code. This coding practice is called procedural or imperative coding and was the dominant trend in mainstream programming languages until recently.
In contrast, a common pattern in functional programming languages, including Scala, consists of using function recursion in place of looping. I.e., functions that call themselves. We can see how this is applied in Scala with the classic factorial example.
This example also introduces the concept of
throwing exceptions. We won’t cover the details yet. Just know it’s a way for our code to handle erroneous situations like passing an invalid input to a function.
Can you write a function called
sumUpTo(x: Int) that uses recursion to sum all integers from zero up to
Again, you can test your solution in Scastie.
An aside on basic pattern matching
I’d like to take a moment to show how the Scala
match construct can be used to implement the conditional logic of the
Here we’re using the
match construct to evaluate different logic based upon the value of
x. Further, you can see we’re not explicitly handling the negative value case. Instead, negative inputs result in throwing a
MatchError because none of the defined
case statements match that input. Also, note how we introduced the
val xp that is only operated on when its value is positive.
This is a basic usage of
match and it can do much more powerful things. We’ll consider more advanced usage of
match in future articles.
Higher Order Functions
As the last part of this introduction to Scala, let’s explore higher-order functions. These are functions that take other functions as arguments. Consider the following example where we want to call a function,
n times using a function
You can see that the
func argument of
callFunctionNTimes is defined as
(String) => Unit. This means that
func is a function that takes a single
String argument and has no return value. We can pass in any function that takes this form, including both
Can you write a higher-order function of the form:
callForIntegersInRange(func: (Int) => Unit, start: Int, end: Int): Unit
Here’s a template.
Again, you can test your solution in Scastie.
Aside: Is it interesting that we can treat
println as both a
(String) => Unit function and a
(Int) => Unit function? We’ll explore how Scala allows us to do this later in this series when we explore a concept called generic types. The short answer is that
println is defined as a function of type
(Any) => Unit and both
Int can be treated as
Thank you for exploring Scala with me! I hope you’ve enjoyed learning a bit about Scala and are excited to learn more. If any parts have been confusing, please let me know and I’ll revise those parts. Further, if there are parts you really liked, let me know and I’ll try to preserve that style in the future. Let me know what you think at firstname.lastname@example.org.
When you’re ready to continue on, the next installment in our series is A brief tour of lists in Scala and algorithmically processing them in SICP exercises.