The latest instance of the course can be found at: O1: 2024
- CS-A1110
- Supplementary Pages
- Style Guide
Luet oppimateriaalin englanninkielistä versiota. Mainitsit kuitenkin taustakyselyssä osaavasi suomea. Siksi suosittelemme, että käytät suomenkielistä versiota, joka on testatumpi ja hieman laajempi ja muutenkin mukava.
Suomenkielinen materiaali kyllä esittelee englanninkielisetkin termit.
Kieli vaihtuu A+:n sivujen yläreunan painikkeesta. Tai tästä: Vaihda suomeksi.
Style Guide
About This Page:
Questions Answered How can I make my program code easier to read and edit? How should I indent my Scala programs and name its components; where in my code should I put line breaks and brackets; are comments a good idea?
Topics: An introduction to programming style and style conventions. Some considerations for formatting Scala code.
Prerequisites: Much of this guide should be understandable after Week 3 of O1 and almost all after Week 4. There are a few points that concern later topics.
Introduction: Why?
Any fool can write code that a computer can understand.Good programmers write code that humans can understand.—Martin Fowler
Good programming style makes programs easier for humans to read and understand. A well-written program is less likely to have bugs, and it’s less work to develop it further.
The computer doesn’t care about readability. In principle, the two class definitions below do basically the same thing:
class
game
(
val
avocados :Team,val aw: Team,val
m1:Int,M2:
Int):
def
Which={if (m1)>this
.
M2
then (Some(
this.avocados))else if M2
>this .m1 then
Some(this.aw) else None
}
/* Each Match object represents a match between two sports
* teams that play one another, each seeking to score goals. */
class Match(val homeTeam: Team, val awayTeam: Team,
val homeGoals: Int, val awayGoals: Int):
/* Returns the winning team, or None in case of a draw. */
def winner =
if this.homeGoals > this.awayGoals then
Some(this.homeTeam)
else if this.awayGoals > this.homeGoals then
Some(this.awayTeam)
else
None
end Match
In practice, the two classes don’t do the same thing, since the first one is so awfully written that you’d never want to use it in a program.
Sure, that example is over-dramatic, given how atrocious the first class definition is. But smaller, accidental violations of good programming style also make programmers’ work considerably harder than it needs to be, or slow them down.
Successful programs survive by evolving: they get edited and developed time and time again. As a program grows to hundreds, thousands, or even millions of lines of code, good programming style becomes increasingly important. Even a lone programmer cannot recall precisely what they’ve done and thought a month or a year ago; it can be surprisingly difficult to modify one’s own poorly written code. Not to mention that many programmers work on legacy code and develop software in teams: one had better hope that the other programmers bothered to make their code easy to work with.
Many things affect how readable and modifiable a program is. High-level design matters, of course: How do you design the program’s structure and break down the whole into logical components? Which classes and methods did you use to model the program’s domain? Is the program designed in imperative or functional “style”? And so forth.
On this page, we’ll restrict the discussion to aspects of style that are superficial but important nevertheless. In the text that follows, programming style refers to “code-writing style”: the decisions we make about how to phrase a design as text. In these terms, the two classes above have the same design but are thoroughly different in programming style.
Style Conventions
A programming language is typically associated with its own style conventions (or coding conventions) that suggest how to format programs written in that language. It’s common that a single language has several alternative sets of conventions that specify somewhat different styles. There is no One True Style.
Scala, too, has its own semi-official style guidelines, which aren’t unanimously accepted or followed by all Scala programmers (or completely up to date with the language, as of mid-2023). This style guide for O1 largely conforms to those general guidelines but expands on them and deviates from them in some details.
In O1, we don’t require you to follow any specific set of coding conventions. But it is important that when you write a program, you write it in some consistent way. Without being consistent, it’s difficult or impossible to reach the goal: clarity.
Some of the other programming courses at Aalto follow somewhat different conventions for Scala than we do in O1. And if you read Scala programs published online, you’ll find that they are written in a variety of styles. You should therefore learn to a) follow some reasonable programming style yourself; and b) tolerate and understand code written in other reasonable styles.
It’s fine to break with convention sometimes, once you know what you’re doing.
How to Improve Readability in Practice?
Here some the key things to consider when formatting your code for readability:
indentation and other use of whitespace;
writing comments where appropriate; and
giving meaningful names to program components.
See below for more detailed suggestions.
Style Basics: Using Whitespace
A program written in, say, Scala has a hierarchical structure. We have files that contain class definitions. Classes in turn define variables and methods. The method definitions contain commands, many of which contain other commands within them.
To make that structure apparent, we format our code accordingly. In Scala and many other languages, consistent indentation is important.
As an example, compare the two code fragments below. They are technically valid (but otherwise pointless) and are essentially similar apart from their indentation and other use of whitespace. The second version is nicer to read than the first. Even so, beginner programmers sometimes indent their code erratically, like in the first version. Don’t do that.
class ExampleClass(val example:AnotherClass,var anotherExample:Int):
def myMethod()=
while this.example.checkIfDone() do
this.doSomething()
if this.example.checkState() then
this.doSomethingElse()
this.anotherExample+=1
else
this.example.changeStuff()
end while
def anotherMethod() =
println(this.example)
// ... other methods ...
end ExampleClass
class ExampleClass(val example: AnotherClass, var anotherExample: Int):
def myMethod() =
while this.example.checkIfDone() do
this.doSomething()
if this.example.checkState() then
this.doSomethingElse()
this.anotherExample += 1
else
this.example.changeStuff()
end while
def anotherMethod() =
println(this.example)
// ... other methods ...
end ExampleClass
Even though it is possible to add indentation for no reason like in the first version above, don’t do that. Even though it is possible to omit indentations from simple methods, don’t do that either.
In Scala, two-space indents are the most common, but some programmers prefer three or four spaces. Consistency matters more than the exact width.
As illustrated above, you can further improve readability with empty lines and additional spaces within lines. It’s generally a good idea to leave a blank line between methods that span multiple lines, at least. Even within a single method, you can consider grouping lines together much like you might group natural language into paragraphs. However, if your method is more than a few lines in length, you should seriously consider splitting it in multiple methods, each dedicated to a single purpose.
Indentations, curly brackets, and old versions of Scala
In O1, we use Scala 3, an up-to-date version of the language. From Chapter 1.7 onwards, you learn that indentations matter in a concrete sense: they have an effect of program structure and thereby on behavior. This has not always been the case.
In Scala 2, and earlier, programmers would write curly brackets in lots of places. The above program, for example, might have been written like this:
// Old-style Scala
class ExampleClass(val example: AnotherClass, var anotherExample: Int) {
def myMethod() = {
while (this.example.checkIfDone()) {
this.doSomething()
if (this.example.checkState()) {
this.doSomethingElse()
this.anotherExample += 1
} else {
this.example.changeStuff()
}
}
}
def anotherMethod() = {
println(this.example)
}
// ... other methods ...
}
Actually, you can even write that in Scala 3, under the appropriate Scala tool settings. But it’s not really how Scala 3 is meant to be written. In O1, we don’t use this style or recommend it.
As for indentations, they have no effect on the behavior of the old-style, curly-bracketed Scala; you can indent (or not) as you please. However, even in the old Scala, the strong recommendation is to indent your code as shown above. In practice, the old style allows unclean code for no particularly good reason.
Style Basics: Commenting Your Code
As Chapter 1.2 pointed out, you can write Comments to help human readers.
Comments can be good or bad. It depends.
Comments that document the purpose of a program component and the interface for using that component are often a good idea:
What concept does a class definition represent? How can a programmer use the class?
What does each of the class’s public methods achieve? What does the method expect as a parameter? What can the method’s caller expect as a return value? Are there any special cases that the caller should be aware of?
Such documentation comments should be written to help programmers use the interface without having to know the underlying implementation. O1’s modules contain many examples.
Documenting methods’ implementations — the commands that are executed when the method runs — is sometimes a good idea. However, you should try to write your code clearly enough that that implementation-clarifying are needed rarely if at all. If your program is well designed, its pieces are sensibly named, and each component’s interface is sufficiently documented, your methods will probably to be short and crisp and require little additional explanation. An explanatory comment in the middle of a method may be a sign that the code should have been written differently! Instead of adding a comment, consider other solutions, such as breaking a complicated line into multiple simpler ones, with intermediate results stored in sensibly named variables.
In O1, you’re not required to comment your code unless specifically requested to do so. You may want to do it nevertheless. Many follow-on courses will require you to write comments, as do many companies and organizations that you might work for in the future.
Style Basics: Naming Things
When choosing an identifier (name) for a program component — class, method,
variable, etc. — try to capture the purpose of that component. number
and value
aren’t generally informative names for variables. Neither are a
, a2
, oppAdv
,
jimmy
, and fsdhafsdkjh
.
(Of course, when experimenting on a tiny piece of code, generic and short names like
number
, a
, or test
are fine.)
Pay attention to the language’s common naming conventions that would confuse readers if violated. For instance, in Scala, start your Scala classes with an upper-case letter and your variables with a lower-case one.
Obviously, you’ll also need to follow any technical restrictions that may apply. You can’t use reserved words as identifiers. The programming language may also impose other limitations; for instance, Scala and many other languages prohibit identifiers that start with a digit.
So far, our discussion of programming style has been mostly generic. Let’s now take a look at some stylistic issues that are particular to Scala.
Scala Style: End Markers
Where you have an indented section of code that consists of multiple lines, you may want to write and end marker where that section ends. (End markers feature in Chapters 1.7 and 2.2, among others.) These markers has no effect on program behavior but may highlight the program’s structure to human readers. Here are two versions of the same code; the first has no end markers whereas the second has four:
class My1Class(val myVariable: Int):
def littleMethod =
this.myVariable + 1
def biggerMethod(number: Int) =
if number > 0 then
number + 1
else
number
class MyClass(val myVariable: Int):
def littleMethod =
this.myVariable + 1
end littleMethod
def biggerMethod(number: Int) =
if number > 0 then
number + 1
else
number
end if
end biggerMethod
end MyClass
In the technical sense, end markers are never required. However, there are some contexts where it is highly recommended to write them for reasons of style. Use an end marker in the following cases:
If the section of code that ends (i.e, a class, object, method,
if
, or some such) contains any blank lines. This is the most common reason to write an end marker.The section is twenty or so lines long, or longer.
The end of the section is indented several levels deeper than its start.
You the programmer have other reasons to believe that the code will be easier to read with the end marker there.
With that recommendation in mind, we could rewrite the above code as follows:
class MyClass(val myVariable: Int):
def littleMethod =
this.myVariable + 1
def biggerMethod(number: Int) =
if number > 0 then
number + 1
else
number
end MyClass
That example is typical in the sense that end markers are most common at the ends of classes, traits, and singleton objects. In well-written code, methods are usually short and don’t need end markers; the same goes for the commands within the method bodies. On the other hand, you are free to add end markers wherever you think they will be beneficial to you or others.
Scala Style: Naming Things
Upper-case vs. lower-case letters
The first letter of a name should tell the reader something about the name’s referrent:
Class names customarily start with an upper-case letter.
Package names are customarily written completely in lower case.
The names of singleton objects typically start with an upper-case latter, although exceptions may be made.
Variable names customarily start with a lower-case letter.
Method names customarily start with a lower-case letter.
In names that consist of multiple words, use upper-case letters to mark the word borders,
as in ClassName
, variableName
, methodName
. Exception: packages completely in lower
case.
Restrictions on names
Don’t use space characters in identifiers.
Beginners are better off avoiding all special characters (+
, &
, {
, etc.) when
naming things. Some special characters have specific meanings in Scala. However, most
special characters can be used in names, which may be useful if you’re looking to
define a method that’s meant to be used as an operator (Chapter 5.2).
The names of library functions such abs
or sqrt
aren’t reserved; neither are the
names of even the most common type names, such as Int
and String
. Just because
you could in principle name one of your own classes Int
doesn’t mean it’s a great
idea, though.
Spaces in names
In Scala, it’s technically possible to have spaces in an identifier if you put the whole identifier in backticks:
val `my fancy variable` = 10my fancy variable = 10 `my fancy variable`res0: Int = 10 my fancy variable-- Error: |my fancy variable |^^ |Not found: my
It’s not customary to do this. If you do, you’re as likely to confuse the reader as anything.
Scala Style: Functions
Some of Scala’s less obvious style conventions concern functions.
A function may be either effect-free (free of “side effects”) or effectful. As noted in Chapter 1.7, it is customary in Scala to highlight this difference with punctuation. The purpose of the following style conventions is to help the program’s reader see which functions may impact on state and which functions never do. This is useful to experienced Scala programmers, at least, and may help beginners, too.
Effect-free functions
A simple, effect-free function can be defined on a single line like this:
def sum(first: Int, second: Int) = first + second
Highlighting the function body on its own line is fine, too:
def sum(first: Int, second: Int) =
first + second
In effectful functions, always put the body on a separate line even if it’s just one line:
def printSum(first: Int, second: Int) =
println("The sum is: " + (first + second))
If the body consists of multiple sequential commands, split it across multiple lines. This applies to effect-free functions and effectful ones alike.
def result(first: Int, second: Int) =
val intermediate = first * 2 + second
intermediate * intermediate
Parameterless functions and round brackets
Some functions don’t take any parameters. In Scala, there are two ways to define such a function: you can define a function with an empty parameter list or a function with no parameter list at all.
If a parameterless function is effectful, give it an empty parameter list (Chapter 2.6). Here’s an example:
def printStandardText() =
println("Note the empty brackets above, which indicate an empty parameter list.")
If a parameterless function is effect-free, give it no parameter list at all. In practice,
this means leaving out the empty brackets, as shown for the mass
method below.
class Projectile(val volume: Double, val density: Double):
def mass = this.volume * this.density
This is a style convention, not a technical requirement enforced by Scala. If you wanted to flout convention, you could give all your parameterless methods an empty parameter list, as is done in some other programming languages. (But don’t.)
If a function has an empty parameter list ()
, you’ll need to write that when you call
it, too: printStandardText()
. This means that the reader of your code will be able to
tell effectful calls from effect-free ones. Moreover, effect-free calls will look just
like variable access, as per the uniform access principle.
Operator vs. dot notation
As noted in Chapter 5.2, there are two ways to call a single-parameter method in Scala:
operator notation and dot notation. For instance, even the so-called “plus operator”
is actually a method on a number object, and you can write either 1 + 1
or 1.+(1)
.
Similarly, you can search for an element in a buffer by writing either numbers.indexOf(10)
or numbers indexOf 10
.
In O1, we generally prefer the dot notation. We only use the operator notation in selected
contexts that involve common data types (such as Int
) and their common, operator-like
methods (such as +
).
You should be aware, though, that the operator notation is somewhat more common outside of O1. Some Scala programmers use it when calling higher-order methods, for instance (as discussed at the end of Chapter 6.3).
Scala Style: Type Annotations
Type inference is an inseparable part of the Scala language. Many type annotations are optional in Scala code, as many (static) types can be automatically inferred. Scala programmers frequently leave types implicit unless there is reason to do otherwise.
When defining variables, it’s common to leave out type annotations. The most common exception to this are parameter variables, which require an explicit type. Even where an annotation isn’t required, a programmer may decide to add one for clarity, and that’s fine.
Most functions don’t require an explicit return-type annotation, but there are exceptions, such as overloaded and recursive functions. On the other hand, you may write a return type on any function; such annotations sometimes clarify the code and may prevent some bugs.
Some Scala programmers explicitly mark the type of all public variables and the return type of all public methods. If you’re building the sort of program where this matters, it may be an excellent idea.
Occasionally, adding a type annotation results in a clearer compile-time error message as the annotation provides additional information to the compiler.
Scala Style: Long Lines vs. Multiple Lines
As a general rule, you should avoid writing excessively long lines. (Programmers disagree
among themselves about what is excessive.) For instance, you can split a long list of
constructor parameters across multiple lines as in the Match
class at the top of this
page.
What about the reverse? Can you omit line breaks where you have a sequence of short commands?
Scala does allow you to do that, but you’ll need to insert semicolons between the commands. For instance, this is valid code:
var number = 0; println(number); number += 1; println(number); number += 10; println(number)
When experimenting on small pieces of code (in the REPL, perhaps), programming style matters less and the semicolons may be a handy shortcut. However, you’ll generally want to avoid that style in proper programs. Writing sequential, imperative commands on separate lines highlights the program’s flow better.
Scala Style: Using this
If an object’s member variable and a method’s local variable have the same name, you
need the keyword this
to refer to the object’s variable; there is a small example in
Chapter 2.2 that illustrates this. (It sometimes makes sense to use multiple variables
with the same name. Of course, you should also consider whether using different names
would be clearer.)
The same chapter also offers this advice:
Even where it’s not required, the word
this
helps indicate that the programmer is using an object’s variables, as opposed to local variables. We recommend that you always usethis
when referring to an object’s own variables, because it makes the code easier to understand.To an extent, this is a matter of taste. If you wish — and if you know what you’re doing — go ahead and leave out any
this
keywords that don’t need to be there. Outside of O1, such omissions are (unfortunately?) very common.
Scala Style: Selecting with if
and match
When writing an if
, consider whether it involves a choice between effects on state
or a choice between effect-free expressions. The basic style is then the same as for
functions: effectful code is split across multiple lines but simple, effect-free code
can be written onto a single line. See below for examples.
Selecting between effects on state
In an effectful if
statement, start each branch on a new line, even if the branch
contains only a single command:
if number > 0 then
println("It's positive.")
else
println("It isn't positive.")
That is, you should avoid this style even though the code works:
if number > 0 then println("It's positive.") else println("It isn't positive.")
Some effectful if
s don’t need an else
branch (Chapter 3.4). In that case, too,
start the then
branch on its own line:
if number > 0 then
println("Print this if the number is positive. Otherwise, print nothing.")
An effectful match
command similarly calls for line breaks:
possibleValue match
case Some(value) =>
println("The value exists.")
println("It is: " + value)
case None =>
println("There is no value.")
Effect-free selection
Many effect-free if
expressions have short and simple then
and else
branches. In
that case, you can write the whole thing onto a single line. In the simplest cases, you
can even have other code, such as a variable definition, on the same line:
val result = if divisor != 0 then dividend / divisor else 0
On the other hand, it’s perfectly valid to split the code across lines if you feel it clarifies things. Here are two ways to do that:
val result =
if divisor != 0 then dividend / divisor else 0
val result =
if divisor != 0 then
dividend / divisor
else
0
That latest multi-line style is particularly apt if the line would otherwise be very long or convoluted:
val result =
if firstConditionIsMet && anotherLongConditionIsMet && cetera then
computeResultUsingThisFunctionWithAReallyLongName(number)
else
computeResultUsingAnotherFunction(number)
You should also split the if
across multiple lines in case either branch contains a
sequence of consecutive commands:
val result =
if conditionMet then
val intermediate = computeIntermediate(myNumber)
Some(computeResult(intermediate))
else
None
In some situations, you may prefer a style where each branch occupies a single line that
starts with if
or else
. You may also want to add space characters for more structure:
val description =
if number < 0 then "negative"
else if number < 100 then "small"
else if number < 10000 then "big"
else "real big"
Again, match
is similar to if
. If you have a single, effect-free expression in each
branch, you can put that expression on the same line with the case
keyword. Either of
these two styles is fine:
val report = possibleValue match
case Some(value) => "The value is: " + value
case None => "No value"
val report =
possibleValue match
case Some(value) => "The value is: " + value
case None => "No value"
Scala Style: Looping with while
and for
The while
keyword (Chapter 9.1) defines loops whose purpose is to affect the program’s
state. Format these loops like you format effectful if
statements: with line breaks.
while conditionHolds() do
keepDoingStuff()
Don’t write this (even though it works):
while conditionHolds() do keepDoingStuff()
Scala has two sorts of for
loops: for
–do
and for
–yield
. In O1’s official
materials, we only use the former (from Chapter 5.5 onwards). These for
–do
loops
are effectful, so put a line break before the loop body:
for element <- myCollection do
doStuff(element)
Effect-free for
–yield
loops
The other sort of for
loop in Scala, for
–yield
, is also common
but does not feature prominently in O1. (It is brought up briefly in
Chapter 6.3 and our Scala Reference.)
These loops are almost always effect-free. When that is the case, and the loop is simple, you may write the whole thing onto a single line like this or this:
val wordLengths =
for word <- myWords yield word.length
val wordLengths = for word <- myWords yield word.length
Scala Style: Function Literals
In Scala, there are different ways to write function literals that define anonymous functions. None of those ways is categorically better than the others or decisively most common among Scala programmers. You should be familiar with the different notations, but you may certainly favor one of them in your own code. See Chapter 6.2 for a discussion.
Summary of Key Points
Programs written in a good style are easier to read and develop. One basic tenet of good style is to format source code so that the program text aids the human reader.
A style convention specifies a particular manner of writing code: how to indent, how to name variables, etc. Different programming languages have their own style conventions; there are usually multiple alternative conventions for any given language.
No matter which style you pick, you should apply it consistently so that you don’t put an extra burden on your reader. On the other hand, it can be okay to deviate from convention if there are reasons to do so in a particular context.
Links to the glossary: programming style, coding conventions; comment; to indent; identifier.
Feedback
Credits
Thousands of students have given feedback and so contributed to this ebook’s design. Thank you!
The ebook’s chapters, programming assignments, and weekly bulletins have been written in Finnish and translated into English by Juha Sorva.
The appendices (glossary, Scala reference, FAQ, etc.) are by Juha Sorva unless otherwise specified on the page.
The automatic assessment of the assignments has been developed by: (in alphabetical order) Riku Autio, Nikolas Drosdek, Kaisa Ek, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Anna Valldeoriola Cardó, and Aleksi Vartiainen.
The illustrations at the top of each chapter, and the similar drawings elsewhere in the ebook, are the work of Christina Lassheikki.
The animations that detail the execution Scala programs have been designed by Juha Sorva and Teemu Sirkiä. Teemu Sirkiä and Riku Autio did the technical implementation, relying on Teemu’s Jsvee and Kelmu toolkits.
The other diagrams and interactive presentations in the ebook are by Juha Sorva.
The O1Library software has been developed by Aleksi Lukkarinen and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.
The pedagogy of using O1Library for simple graphical programming (such as Pic
) is
inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and
Krishnamurthi and Picturing Programs by Stephen Bloch.
The course platform A+ was originally created at Aalto’s LeTech research group as a student project. The open-source project is now shepherded by the Computer Science department’s edu-tech team and hosted by the department’s IT services. Markku Riekkinen is the current lead developer; dozens of Aalto students and others have also contributed.
The A+ Courses plugin, which supports A+ and O1 in IntelliJ IDEA, is another open-source project. It has been designed and implemented by various students in collaboration with O1’s teachers.
For O1’s current teaching staff, please see Chapter 1.1.
Additional credits appear at the ends of some chapters.
The class definition contains blank lines, so we write an end marker for it. The methods and the
if
expression don’t need one though.