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

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-2024). 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 implementation-clarifying comments 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 have 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

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.

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.

    • However, variables that are meant as constants are capitalized. A constant’s value is known independently of any program run and represents some “permanent” information. Using constants instead of “magic numbers” is good style (see, e.g., Chapter 2.6).

  • 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 +).

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.

Many Scala programmers explicitly mark the type of all public variables and the return type of all public methods. Especially if you’re building serious software, this is a good 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 use this 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 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 ifs 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: fordo and foryield. In O1’s official materials, we only use the former (from Chapter 5.5 onwards). These fordo loops are effectful, so put a line break before the loop body:

for element <- myCollection do
  doStuff(element)

Effect-free foryield loops

The other sort of for loop in Scala, foryield, 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, Onni Komulainen, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, Joel Toppinen, 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, Juha Sorva, and Jaakko Nakaza. 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; 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.

a drop of ink
Posting submission...