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. Myös suomenkielisessä materiaalissa käytetään ohjelmien koodissa englanninkielisiä nimiä kurssin alkupään johdantoesimerkkejä lukuunottamatta.
Voit vaihtaa kieltä A+:n valikon yläreunassa olevasta 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 code; which functions should have brackets; what about lower- and upper-case letters?
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?
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 clear about readability. In principle, the two class definitions below do basically the same thing:
class
game
(
val
avocados :Team,val vi: Team,val
m1:Int,M2:
Int){def
Which={if(m1>this
.
M2)Some(
this.avocados)else if(M2
> this.m1)
Some(this.vi) 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 the match
* is a draw. */
def winner = {
if (this.homeGoals > this.awayGoals)
Some(this.homeTeam)
else if (this.awayGoals > this.homeGoals)
Some(this.awayTeam)
else
None
}
}
In practice, the two classes don’t do the same thing, since the first class is so awfully written that a 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 can also make the programmer’s work harder or at least slow them down.
Successful programs survive by evolving: they get edited and further 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 thought and done 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 has the programmer designed the program’s structure and broken the whole down into logical components: Which classes and methods have been identified to model the program’s domain? Is the program designed in imperative or functional “style”? And so forth.
On this page, we’ll restrict our discussion on relatively superficial aspects of style, which are nevertheless also important. In the text that follows, programming style refers to “code-writing style”: the decisions we make about how to phrase a particular program design as text. In these terms, the two classes above have the same design but thoroughly different in programming style.
Style Conventions
Each 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 of writing programs.
Scala, too, has its own semi-official style guidelines, which isn’t unanimously accepted or followed by all Scala programmers. This style guide for O1 largely conforms to those general guidelines but deviates from it in some details.
There is no One True Style to write programs in a language. 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 react 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:
- follow some reasonable programming style yourself; and
- tolerate and understand code written in other reasonable styles.
It’s fine to break with convention sometimes, once you know what you’re doing and judge it as appropriate,
How to Improve Readability in Practice?
When formatting your code for readability, some of the key considerations are:
- 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. There are files that contain class definitions. Those classes in turn define variables and methods. The method definitions contain commands, many of which are a combination of other commands.
We can format our code to make that structure apparent. In Scala and many other languages, the convention is to indent lines of code.
As an example, compare the two (otherwise pointless) fragments of code. They only differ from each other in how they’re indented and use whitespace (line breaks and space characters). You’ll probably agree that the second version is nicer to read than the first.
class ExampleClass(val example:AnotherClass,var anotherExample:Int){
def myMethod()={
while(this.example.checkIfDone()){
this.doSomething()
if(this.example.checkState()){
this.doSomethingMuuta()
this.anotherExample+=1
}else{
this.example.changeStuff()
}
}
}
def anotherMethod()={
println(this.example)
}
// ... other methods ...
}
class ExampleClass(val example: AnotherClass, var anotherExample: Int) {
def myMethod() = {
while (this.example.checkIfDone()) {
this.doSomething()
if (this.example.checkState()) {
this.doSomethingMuuta()
this.anotherExample += 1
} else {
this.example.changeStuff()
}
}
}
def anotherMethod() = {
println(this.example)
}
// ... other methods ...
}
Beginner programmers sometimes leave their program unindented as in the first version above or indent their code erratically. Don’t do that.
In Scala, indentations are customarily two spaces wide.
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 seriousy consider splitting it in multiple methods, each dedicated to a single purpose.
Style Basics: Commenting Your Code
The fact that you can write Comments that help human readers came up already in Chapter 1.2.
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?
Documentation comments and Scaladoc
If you write your Scala comments in a particular notation, you can use the Scaladoc tool to generate documentation pages automatically (Chapter 3.2). For more on Scaladoc, see Alvin Alexander’s tutorial.
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 but the urge to write such a comment may also indicate a problem that would be best solved differently. In general, 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 components 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.
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 very 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 starting an identifier 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: 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.
- The names of singleton objects typically start with an upper-case latter, although exceptions may be made (e.g., when the singleton is intended as package object; Chapter 5.2).
- 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:
ClassName
, variableName
, methodName
. Exception: package names are written completely
in lower case.
Restrictions on names
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<console>:11: error: not found: value my my fancy variable ^
It’s not customary to do this. If you do, you’re as likely to confuse the reader as anything.
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.
Scala Style: Defining Functions
Some of Scala’s more unusual style conventions concern the function definitions.
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.
The equals sign
Always write an equals sign between the parameter list and the function body.
def sum(first: Int, second: Int) = first + second
Omitting equals
Scala does in fact allow you to leave out the equals sign in some cases (
i.e., if your function is supposed to return Unit
). For instance, this
function does work but you should not write your code like this:
def greet(greeting: String) {
println(greeting)
}
Write this instead:
def greet(greeting: String) = {
println(greeting)
}
Effect-free functions
In effect-free functions, it’s customary to leave out the curly brackets when possible. They are, however, required in case the method body consists of multiple sequential commands, as here:
def result(first: Int, second: Int) = {
val intermediate = first * 2 + second
intermediate * intermediate
}
This function has a single-line body, so the curly brackets are unnecessary and are customarily not written:
def sum(first: Int, second: Int) = {
first + second
}
A simple function such as sum
would normally be defined on a single line, as shown
earlier.
Effectful functions
In effectful functions, it is customary to always write the curlies, line breaks, and indentation, even if the method body is just a single line:
def printSum(first: Int, second: Int) = {
println("The sum is: " + (first + second))
}
Summary
This table, replicated from Chapter 1.7, summarizes the conventions just listed. You can mouse over the underlined parts for some clarifications.
Does the function affect state? | Does it return a value? | The term we use in O1 | Write an equals sign in front of the function body? | Write curly brackets around the body? | Use line breaks and indentations? |
---|---|---|---|---|---|
Never | Yes | Effect-free function | Yes | At least if the body has multiple commands. | Always where you have curly brackets. Also used for splitting long one-liners. |
No | (If a function doesn’t have an effect on program state or return a value, it can’t be very useful, barring some special circumstances. You won’t need to write such an empty function in O1.) | ||||
At least sometimes | Yes | Effectful function | Yes | Yes | Yes |
No | Effectful function | Yes (Your code will work without it, but write it anyway. Required in later Scala versions.)` | Yes | Yes |
The simple option
In O1, it’s acceptable to put an equals sign, curly brackets, and indentations in all your function definitions. But you can try following the conventions listed above.
A further consideration: parameterless functions
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, it’s customary to give it an empty parameter list (Chapter 2.6). Here is an example:
def printStandardText() = {
println("Note the empty brackets above, which indicate an empty parameter list.")
}
If a parameterless function is effect-free, it’s customary to 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
}
Note that 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 parameterless (as is done in some other programming languages).
Whether a function has an empty parameter list or not affects how you may call the function, as discussed below.
Scala Style: Calling Functions
Calling a parameterless function
When you call a parameterless function, you need to heed whether it’s truly parameterless (and effect-free, if the code adheres to convention) or if it has an empty parameter list (and is effectful).
When you call a function that has no parameter list at all, you must write only the function’s name. Do not write empty brackets after the name:
someProjectile.mass // works
someProjectile.mass() // doesn’t work
If the function has an empty parameter list, it’s technically possible to either write the empty brackets or omit them as you call the function. Convention dictates that you should prefer the form with the brackets, which highlights the effect on state:
printStandardText() // works; this is the recommended form
printStandardText // works but is strongly discouraged; won’t work in later Scala versions
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 can 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.
Occasionally, adding a type annotation results in a clearer compile-time error message as the annotation provides additional information to the compiler.
Scala Style: Using Whitespace
As the grotesque program at the top of this page demonstrates, Scala is pretty liberal about the use of whitespace; most programming languages are. There are some restrictions, though: you can’t add space in the middle of a reserved word, for example, nor is a line break allowed in the middle of a (regular) string literal, etc. If you want the rules in full, you’ll find them in the official definition of the Scala language.
As a general rule, you should avoid writing excessively long lines. (Programmers disagree
among themselves as to what is excessive.) For instance, you can split a long list of
constructor parameters across multiple lines ass shown in the Match
class at the top
of the 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 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 when it’s not required, the word
this
emphasizes where the programmer uses an object’s variables, as opposed to where they use 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 common.
Scala Style: if
and match
When selecting, consider whether you’re dealing with a choice between effects or a choice between effect-free expression. The basic idea is the same as for functions: effectful code is split across multiple lines.
Selecting between effects on state
If you have an effectful if
statement, surround each branch in curly brackets, even if
the branch contains only a single command:
if (number > 0) {
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) println("It's positive.") else println("It isn't positive.")
Some effectful if
s don’t need an else
branch (Chapter 3.4). Just omit that branch
and write the optional effect in curly brackets:
if (number > 0) {
println("Print this if the number is positive. Otherwise, print nothing.")
}
An effectful match
command similarly calls for line breaks and indentations:
possibleValue match {
case Some(value) =>
println("The value exists.")
println("It is: " + value)
case None =>
println("There is no value.")
}
Effect-free selection
If you have an effect-free if
, check if either of its branches contains multiple
consecutive commands. If so, put both branches in brackets as described for the
effectful case above.
However, if you have two effect-free branches with a single command in each, you can combine them into a single line:
val result = if (divisor != 0) dividend / divisor else 0
In case such a line would end up being very long, it’s better to split it:
val anotherResult =
if (firstConditionIsMet && anotherIsMetToo && oneMoreLongCondition && cetera)
computeResultUsingThisFunctionWithAReallyLongName(number)
else
computeResultUsingAnotherFunction(number)
The same logic goes for match
. Where you have a single, effect-free expression, you can
write it on the same line as the case
keyword.
val report = possibleValue match {
case Some(value) => "The value is: " + value
case None => "No value"
}
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 can certainly favor one of them in your own code. See Chapter 6.2 for a discussion.
Scala Style: Looping with while
, do
, and for
The while
and do
keywords (Chapter 8.3) define loops whose purpose is to affect
the program’s state. Moreover, all the for
loops that appear in O1’s official materials
are likewise effectful.
Format such effectful loops like you format effectful if
statements: use line breaks
and curly brackets. For instance, here is a while
loop:
while (conditionHolds()) {
keepDoingStuff()
}
Don’t write this (even though it works):
while (conditionHolds()) keepDoingStuff()
Summary of Key Points
- Programs written in a good style are easier to read and develop. One basic aspect of programming style is the formatting of 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 practices 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.
The Final Word
Any fool can write code that a computer can understand.Good programmers write code that humans can understand.—Martin Fowler
Feedback
Credits
Thousands of students have given feedback that has 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, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, 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 was created by Nikolai Denissov, Olli Kiljunen, and Nikolas Drosdek with input from Juha Sorva, Otto Seppälä, Arto Hellas, and others.
For O1’s current teaching staff, please see Chapter 1.1.
Additional credits appear at the ends of some chapters.