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.


Chapter 1.8: Functions, Types, and Errors

../_images/person01.png

More on Function Calls and the Call Stack

Take a look at the program code below. It features a custom function greatestDistance that takes in the x and y coordinates of three points and determines the longest distance between any two of those points. The function uses of another custom function, distance, which saw in the previous chapter; it calculates the distance between two given points.

def distance(x1: Double, y1: Double, x2: Double, y2: Double) = hypot(x2 - x1, y2 - y1)

def greatestDistance(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double) =
  val first  = distance(x1, y1, x2, y2)
  val second = distance(x1, y1, x3, y3)
  val third  = distance(x2, y2, x3, y3)
  max(max(first, second), third)

There are two senses in which a function can be “inside” another:

You may nest a function-calling expression within another.

Also, you may call a function within the body of another function. Here, greatestDistance calls distance (three times).

The following animation elaborates.

Before Our Next Coding Session, Here’s a Summarizing Exercise on Functions

I like it that the animations show how the computer “thinks”. I mean, it doesn’t really think but just mechanically follows each function, starting with the inner ones and proceeding outwards. It’s easier to read code when you can think like the computer.

As we move towards programs that are more complex and more delightful, it’s important that you learn to make reliable inferences about how a given piece of code works when it runs. Or about a piece of your own code that fails to work right.

Study the following program. It doesn’t accomplish anything useful as such, but it serves as an exercise in function calls, return values, and printing.

What does the command test2("cat") print out? Enter the entire output below, with the lines in the correct order. (Don’t enter the lines of code that do the printing, please.)

Consider that same command that calls test2. During that command’s execution, how many times does a new frame get created that is associated with either yell, test1, or test2? (This means that the lowermost frame visible in the animation, which initiates the execution, doesn’t count.)

Consider the same command. During its execution, how many calls to yell, test1, or test2 take place, in total?

Again, consider the same command. During its execution, what is the greatest number of frames that exist in the call stack simultaneously and are associated with either yell, test1, or test2? (Note: the lowermost frame visible in the animation, which initiates the execution, does not count.)

Stay with the same command. During its execution, what is the greatest number of calls to yell, test1, or test2 that are simultaneously in progress?

Can different frames on the call stack contain variables that have the same name with one another?

If you need convincing that it pays off to know about the call stack and its frames when reasoning about program code, the following questions may help.

Consider the following function:

def experiment(number: Int) =
  var a = number
  println(a)
  a = a + 1
  println(a)
  a - 1

And the following usage example:

val a = 10
println(experiment(100))
println("The value of a is now: " + a)

When we instruct the computer to run these three lines of code (in the REPL, say), what does the third of those three lines of code print out and why?

Especially if you find this question hard, consider the frames that get created when the code is run and the variables within each frame.

def compute(x: Int) = 2 * x + helper(10) + 100

def helper(y: Int) = x + y

Given what you know about functions, methods, and the call stack, assess the two functions above and the claims about them below. Select each correct claim. Make an educated guess if you need to and, once again, read the feedback you receive. If you want, you can also try the functions by copying their definitions into the REPL.

Your friend is working on a Scala function called swapContents, which should work as follows:

var a = 1a: Int = 1
var b = 2b: Int = 2
swapContents(a, b)ares0: Int = 2
bres1: Int = 1
var c = 3c: Int = 3
swapContents(a, c)ares2: Int = 3
cres3: Int = 2

It’s your friend’s intention that whoever calls swapContents can pass in any two var variables that hold Ints and that have been defined outside the function. Calling the function will then exchange those variables’ values with one another.

Here are two drafts of such a function:

def swapContents(number1: Int, number2: Int) =    // Draft 1
  val temporaryStore = number1
  number1 = number2
  number2 = temporaryStore
def swapContents(a: Int, b: Int) =                // Draft 2
  val temporaryStore = a
  a = b
  b = temporaryStore

Neither of these drafts works. Why not? Select all the reasons that apply. Assess the attempts in the light of all the code and animations you have seen. Experiment in the REPL, and discuss the matter with your student pair or the course staff if you want. Remember to read the automatic feedback.

Programming Exercise: leaguePoints and teamStats

In this assignment, you’ll create two functions, using one as a building block for the other.

Programming is like building with smart Lego that you design yourself!

—origin unknown

Task description

In the file week1.scala, write two effect-free functions that compute a sports team’s league points, given the team’s results.

The first of the functions should match this specification:

  • Its name is leaguePoints.

  • As parameters, it expects the number of wins and the number of draws that the team has played. In that order, as integers.

  • It returns — but does not print! — the team’s total league points as an integer. A win is worth three points and a draw is worth one point (and a loss is worth no points at all).

The second function should match this specification:

  • Its name is teamStats.

  • As parameters it expects, in order, the name of the team (a string) and the numbers of wins, draws, and losses (integers).

  • It returns (but does not print!) a string in the format "Name: X/N wins, Y/N draws, Z/N losses, P points". For instance, the call teamStats("Liverpool FC", 8, 7, 7) should return the string "Liverpool FC: 8/22 wins, 7/22 draws, 7/22 losses, 31 points"

Workflow

First, create the function leaguePoints. Follow the workflow suggested in Chapter 1.7’s programming assignments. Don’t forget to test that your function works before you continue.

Then, in the same manner, write and test the function teamStats. Here are a few additional tips:

  • When writing a function body with multiple commands, remember to indent it.

  • To get the team’s points total, call the leaguePoints function you created earlier.

  • Your code will be more readable if you use a local variable to store the total number of games played by the team.

Submit your solution only when you have written and tested both functions.

A+ presents the exercise submission form here.

More Exercises

Additional practice: Feet and inches, the other way around

This exercise continues the imperial units theme from Chapter 1.7. You’ll create three one-liner functions, the first of which will be useful for implementing the other two.

First, write an effect-free function toInches that takes a length in meters and returns the equivalent number of inches. An inch is 2.54 cm. Here are some examples:

toInches(1.8)res4: Double = 70.86614173228347
toInches(0.0254)res5: Double = 1.0

Then create two functions that you can use in combination to convert a given number of meters into whole feet and leftover inches. For instance, 1.8 meters is five feet and about eleven inches. Here’s how the two functions should work:

wholeFeet(1.8)res6: Double = 5.0
remainingInches(1.8)res7: Double = 10.866141732283475

Make use of toInches when you implement these functions. Revisit Chapter 1.6 to review the tools available in package scala.math.

Enter all three functions in week1.scala.

A+ presents the exercise submission form here.

A challenge version of see-above

Find out on your own how to use pairs (pari) in Scala. Apply what you learned to solve the previous problem in a new way.

Create toInches as suggested above. Then implement a function toFeetAndInches that combines the functionality of wholeFeet and remainingInches. The function should return the numbers that represent feet and inches as a pair. Like so:

toFeetAndInches(1.8)res8: (Double, Double) = (5.0,10.866141732283467)
toFeetAndInches(0.0254)res9: (Double, Double) = (0.0,1.0)

(We’ll have more to say about pairs in Chapter 9.2.)

A+ presents the exercise submission form here.

Programming Exercise: verbalEvaluation

Let’s get back to course grades and create an effect-free function that produces a verbal assessment of a student’s work in our imaginary example course. Here’s a template for the function:

def verbalEvaluation(projectGrade: Int, examBonus: Int, participationBonus: Int)
  val descriptions = Buffer("failed", "acceptable", "satisfactory", "good", "very good", "excellent")
  // PLEASE FILL OUT THIS PART OF THE SOLUTION. YOU CAN REMOVE THIS COMMENT.

The function should work as follows:

  • The three parameters represent a project grade and bonuses for an exam and participation, exactly as in overallGrade from Chapter 1.7.

  • Instead of an integer, this function should return a string description that matches the student’s overall grade. For instance, a grade of two is described as "satisfactory" and a grade of five as "excellent".

Implement the function properly:

  • Copy the above code template into week1.scala.

  • There is a small but serious error in the given template code! You’ll need to fix it.

    • IntelliJ’s description of the error isn’t too fantastic, for a reason that we’ll discuss at the end of this chapter. But the fix you need to apply is very simple.

  • Fill in the function body. Use the buffer that’s already defined for you, along with the overallGrade function from Chapter 1.7. Once you combine these two, not much code is needed.

A+ presents the exercise submission form here.

Programming Exercise: doubleScore

The next function is effectful: it modifies the contents of a given buffer. (In this respect, it is similar to Chapter 1.6’s removeNegatives.)

Task description

Let’s consider an imaginary game where multiple players compete and collect points. As the game progresses, a player’s score may occasionally double, but players may also suffer losses. In this assignment, you’ll create an effectful function in week1.scala that doubles a given player’s score. The function should work as follows:

  • Its name is doubleScore.

  • As its first parameter, it receives a reference to a Buffer whose elements are integers that represent the current scores of each player.

  • As its second parameter, it receives an integer that determines which player’s score should be doubled: one means the first player’s score, two the second player’s, and so forth.

  • It modifies the contents of the given buffer so that the targeted player’s score becomes twice what it was before.

Here’s an example scenario:

val scoresOfEachPlayer = Buffer(2, 10, 5, 2)scoresOfEachPlayer: Buffer[Int] = ArrayBuffer(2, 10, 5, 2)
doubleScore(scoresOfEachPlayer, 3)doubleScore(scoresOfEachPlayer, 4)scoresOfEachPlayerres10: Buffer[Int] = ArrayBuffer(2, 10, 10, 4)

Instructions and hints

  • You’ll need to indicate that the first parameter is a buffer whose elements are integers. Use square brackets around the buffer’s type parameter as in Chapter 1.5.

  • The second parameter identifies the target player. It uses a one-based numbering scheme, whereas buffers’ indices start from zero (Chapter 1.5). You’ll need to take this into account.

  • Don’t worry about special cases such as what happens if someone passes in a player number that’s too large or too small. O1’s programming assignments will tell you explicitly when you’re expected to handle invalid inputs.

  • You may assume that each player’s score is a positive integer.

  • The function doesn’t need to return anything.

  • A single assignment command that targets the right index in the buffer will do for a function body.

A+ presents the exercise submission form here.

Programming Exercise: penalize

Let’s stay with the same imaginary game. You’ll now create an effectful function that reduces a player’s score. It should work like this:

  • Its name is penalize.

  • As in the previous function, the first parameter refers to a buffer that contains the players’ scores and the second parameter is the number of the target player.

  • The third parameter indicates the size of the penalty: how many points should be subtracted from the target player’s score. You can assume that this is a positive number.

  • However, the rules of the game dictate that a player’s score can never drop to zero or below; a player will always have at least a single point. If a player receives a penalty that would violate this rule, the player’s score will only be reduced down to one.

  • The function returns an integer: how many points were actually removed from the target player.

Here’s a usage scenario:

val scoresOfEachPlayer = Buffer(2, 10, 5, 2)scoresOfEachPlayer: Buffer[Int] = ArrayBuffer(2, 10, 5, 2)
penalize(scoresOfEachPlayer, 2, 3)res11: Int = 3

We subtract three from the player number two’s score. The full penalty could be applied, and the return value indicates that three points were successfully removed.

A look at the scores buffer confirms that the reduction happened. The second score has dropped to seven:

scoresOfEachPlayerres12: Buffer[Int] = ArrayBuffer(2, 7, 5, 2)

Let’s give the same player a twelve-point penalty:

penalize(scoresOfEachPlayer, 2, 12)res13: Int = 6
scoresOfEachPlayerres14: Buffer[Int] = ArrayBuffer(2, 1, 5, 2)

However, only six points were successfully removed...

... because a player must always have at least one point.

If it seems clear to you how to solve this assignment, feel free to go ahead and write the entire solution right now. Otherwise, we recommend approaching it in two stages:

Stage 1 of 2: don’t worry about the return value

For now, just write a version of the function that reduces the target player’s score in the buffer. Don’t concern yourself, yet, with whether the function returns the right value.

You can use Scala’s library function min (Chapter 1.6) to determine how many points can be removed. Another approach is to use max. There are other ways to make the function work, too, and you’re free to use any of them.

Test your function to ensure that it works!

Stage 2 of 2: sort out the return value

You already know that in Scala, a function return value is determined by the last command that is executed as part of the function call. A beginner’s first sketch for this algorithm might thus look something like this:

  1. Reduce the player’s score, but not below one.

  2. Calculate the number of points that were removed and return that result.

The problem is that to compute the return value, we need both the third parameter (the size of the penalty) and the player’s original score. But after we’ve applied the reduction, the original score is no longer stored anywhere and there’s no way we can compute the return value at Step 2.

Another attempt:

  1. Calculate the number of points that can be removed and store that result.

  2. Reduce the player’s score by that amount.

  3. Return the amount stored at Step 1.

This version is better because it computes the future return value before applying the penalty.

To implement this improved algorithm, you’ll need to store the actual size of the penalty at Step 1. That’s well within your grasp: use a local variable.

Hints

Bear in mind that a multi-line function returns the value of the expression that was evaluated last. Such an expression can be simply the name of a variable.

If you want, you can view the following animation. It presents one way to solve this assignment. (The animation does not show the program code; that’s something you’ll have to write yourself.)

There is something worth mentioning in the animation apart from the solution itself. It illustrates that penalize receives a reference to a buffer, not a copy the buffer with identical but separate contents. This is why we can observe a change in the buffer through another reference that points to the same buffer.

A+ presents the exercise submission form here.

Functions with No Parameters

All the functions we’ve discussed so far have taken at least one parameter. It’s also possible to define a function that takes no parameters.

def onePlusOne = 1 + 1

No round brackets, no parameter list.

That function always returns the same number:

onePlusOneres15: Int = 2
onePlusOneres16: Int = 2

A function definition with no parameters looks a lot like a variable definition. Compare these two:

def myFunc = sqrt(500)
val myVal  = sqrt(1000)

Let us then enter some further code:

println(myFunc)
println(myFunc + myFunc)
println(myVal)
println(myVal + myVal)

Consider what happens when the computer executes the above code. Select the correct claims in the list below. Experimenting in the REPL may help you work out the answers if you’re uncertain.

Different Kinds of Errors

How long did it take to write the function?
Not long.
How long did it take to fix the function?
Very long.

Ninety percent of your time will be spent searching for errors in the ten percent of code that you last wrote.

—origin unknown

Finding and fixing errors is an essential activity in programming and takes up a large chunk of programmers’ time. Now that you have your hands dirty writing program code, it’s good to learn the main types of errors.

Errors at compile time

Compilation-time errors (käännösaikainen virhe) can be detected automatically even before the program is run. The name refers to how these errors can be spotted by auxiliary programs called compilers. (Compilers convert your program code into a form that is more readily executable by a computer. More on that in Chapter 5.4.)

Compile-time error messages result from mistakes such as incorrect punctuation and (in Scala) attempting to assign a value of an incompatible type to a variable. Many compile-time errors are syntax errors (syntaksivirhe): they indicate violations of the programming language’s syntactical rules (grammar).

For the experienced programmer, most compilation-time errors are quick and easy to fix. For the beginner, too, this tends to be the least problematic category of errors.

In Chapter 1.7, we discussed how IntelliJ highlights some errors in the editor and displays error messages in the Build tab. All of those are examples of compilation-time errors (even though IntelliJ red-alerts many of them instantly in the editor already before full compilation).

Errors at runtime

Runtime errors (ajonaikainen virhe) are more irksome: they only reveal themselves when the program is being executed and may not show up for all input values.

The classic example of a runtime error is division by zero: if it transpires that we’ve used an expression where the denominator evaluates to zero, a runtime error occurs. The error will “crash” the program (i.e., abruptly abort the program’s execution) unless we’ve specifically prepared for that contingency.

Indexing errors are another example. You saw some of them in Chapter 1.5 when trying to use too-large or too-small integers to access a buffer’s elements.

Exception (poikkeus) is an alternative name for some runtime errors.

We’ll return to runtime errors and program crashes in Chapter 4.1.

Note that in the REPL, the distinction between compile-time and runtime errors is blurry, because our code is (first) compiled and (then) run as soon as we type it in.

Logical errors

A logical error (looginen virhe) is what we call it when our program “works” in the technical sense — it runs without crashing — but does something other than what was intended. For example, using the wrong arithmetic operation is a logical error.

Some logical errors are easy to spot by looking at the code or observing what the program does. Others are much harder. In any case, it’s up to the programmer to find them, as these errors don’t produce error messages.

On Data Types and Scala

Let’s round off Week 1 with a few observations on how data types are used in Scala programs and how this has already manifested itself in the code that we’ve written. The gray-bordered box below sets up the topic but isn’t strictly necessary for our purposes in O1. Feel free to skip it if you’re in a hurry. The text that follows the box is more crucial.

On Type Systems

The nature of a programming language is strongly shaped by its type system (tyyppijärjestelmä): the general rules that govern data types of program components and how those types affect the way people program.

The details of type systems aren’t part of O1, nor is the theory behind them, but we’ll take an educational dip into some basic concepts.

Type safety

Scala is a very type safe (tyyppiturvallinen) language. Each value in a Scala program has a specific data type, and that type determines which operations we can apply to the value. Integers can be used in arithmetic, strings can be concatenated, and buffers can receive new elements; on the other hand, an integer can’t store elements, and an attempt to do such a thing brings a timely error message.

The classic example of a programming language with an unsafe type system is the C language. In C, the programmer can write code that “goes against the types”, leading to behavior that depends on context and can be, in some cases, unpredictable. An unsafe type system allows alternative ways of solving problems but increases the likelihood of errors — errors that can slow down even accomplished programmers and damage the final product.

Static vs. dynamic typing

In Chapter 1.2, we noted that programs have both a static and a dynamic nature, a fact that is also highlighted by this ebook’s animations.

The same duality is also reflected in how programming languages handle types.

Scala is statically typed (staattisesti tyypitetty): each part of a Scala program has a type that is well-defined already in the program’s static form, the program code. For instance, each variable and expression has a specific data type that can be determined by looking at the code.

Among other things, this means that our tools can warn us about invalid code — like attempting to pass an integer as a parameter where a string is required — even before we run our program.

(Using a value with the wrong type — say, as a function parameter — might sound like a silly mistake. However, such mistakes are far more common and understandable than you might think, even in code written by professional programmers. You’ll notice this yourself sooner or later.)

Static typing can also lead to more efficient (faster) programs support better programming tools. These benefits are especially prominent in larger programs that aim for high quality and reliability.

In a dynamically typed (dynaamisesti tyypitetty) language, the parts of program code don’t have types as such. For instance, in the (most commonly used form of the) Python programming language, variables don’t have types, and you can assign any kind of value to any variable. Exactly what type of value gets assigned to a particular variable may be determined by events that happen during a particular program run and influenced by user input.

Dynamic typing is more flexible in some respects and makes some aspects of programming convenient; a dynamic typed language may also be simpler than a statically typed one. The downsides of dynamic typing include an increased risk of programming errors and the related problem that many errors cannot be spotted without running the program on various inputs.

Programmers disagree, sometimes vehemently, about the relative merits of static and dynamic typing. The topic has been the cause of many a religious war, civilized conversation, and minor dispute.

Type annotations

In many statically typed languages (such as Java), the programmer writes type annotations all over program code. In these languages, when you define a variable, you’ll always (or usually) also write down the variable’s data type. The type of most or all return values must also be explicitly stated.

On the other hand, in dynamically typed languages such as (regular) Python, such type annotations don’t feature at all.

Scala is statically typed, but we haven’t written many type annotations in our programs. It was only recently, in Chapter 1.7, that we first paid proper attention to the matter, when we introduced the need to annotate each parameter variable with a type. We’ve mostly been able to ignore type annotations so far, because Scala can automatically infer much of the static type information inherent in a program. As a result, Scala often lets us write code with the same convenience that’s usually associated with dynamic typing while providing the guarantees that static typing affords.

Type inference in Scala

There are certain parts of a Scala program where type annotations are required. The parameter variables of functions are one such part: we follow each parameter’s name with a colon and a data type.

It’s not just parameters that have data types in Scala, but the types of other constructs can usually be determined automatically by Scala’s type inference (tyyppipäättely).

For example, we have defined variables like this:

val number = 123
val text = "The number is " + number + "."

Those commands are in fact shortened versions of these type-annotated ones:

val number: Int = 123
val text: String = "The number is " + number + "."

The shorter versions work, because the types of the variables can be readily inferred from the values we assign to them. It’s perfectly legal to write the type annotations — as we did right there — but it’s unnecessary, and we don’t usually bother.

A familiar function definition appears below. In this piece of code, too, we’ve actually omitted a data type and left it for the computer to determine:

def average(first: Double, second: Double) = (first + second) / 2

The above is short for this:

def average(first: Double, second: Double): Double = (first + second) / 2

The final Double annotation after the parameter list indicates the type of the return value: this function takes in two decimal numbers and also returns a decimal number. However, the return type can be automatically inferred from the expression (first + second) / 2, given that the parameters’ types are known. So we can skip writing that bit if we want.

Later in O1, we’ll come across some other circumstances where it’s necessary to write explicit type annotations in Scala code.

About that earlier error

Remember the error in the given template for verbalEvalution? This one:

def verbalEvaluation(projectGrade: Int, examBonus: Int, participationBonus: Int) 
  val descriptions = Buffer("failed", "acceptable", "satisfactory", "good", "very good", "excellent")
  // ...

IntelliJ’s editor highlights the end of the def line at the top. The error message opines: Missing return type.

Yeah, well, it’s true that you could write a return type there, too, but the actual problem is the missing equals sign.

The more general lesson here is this: although the computer can spot certain errors for us, its decriptions of the problem and what to do about it aren’t always apt. Some of the suggestions you’ll see in error messages do hit the mark, but ultimately, it’s the programmer’s responsibility to analyze the situation and apply the right fix.

Summary of Key Points

  • A function can call another function.

    • When that happens, a new frame is added to the top of the call stack. The calling function’s execution is suspended while it waits for the other function to finish. Only the top frame of the call stack is actively used at any given time.

    • You can write a function that performs a subtask and use it within another custom function.

  • Programs can have compilation-time errors, runtime errors, and logical errors.

  • Scala tools can automatically infer the types of many program components automatically. However, there are a few places in Scala programs where type annotations are required; parameter variables are the most obvious example.

  • Links to the glossary: function, function call, call stack, frame, local variable; error; type annotation, type inference.

What Now and What Next?

You can now write your own program components — functions — constructing them from assorted materials such as numbers, buffers, and other functions. But we’re still a bit of ways from creating an entire program with multiple components that work in unison.

There are a number of approaches to building a larger program. We’ll adopt one good approach in Week 2.

On Academic Integrity and AI Tools

Now that you have a feel for programming, it’s the right time to revisit the course policies mentioned in Chapter 1.1. As stated there, you can do O1’s assignments alone or in collaboration with a pair (but not split the assignments between the members of a pair). You are free to discuss the assignments with other students besides your pair.

But each student or pair must produce their own program code. Each member of a pair must understand all the code that the pair produced. It is strictly forbidden to copy code from others, to provide a copy of your code to another student, or to distribute solutions online. This also applies to any example solutions provided by the course.

It is forbidden to use “artificial intelligence” (AI; e.g., ChatGPT, GitHub Copilot, JetBrains AI, Bing) to solve O1’s assignments. Submitting solutions produced by such tools counts as plagiarism just like copying solutions from a human does. The course materials are the property of their creators, you do not have permission to enter them into AI tools or other third-party services.

Here are a few examples of what you are allowed to do with a tool such as ChatGPT (but remember that its responses may be incorrect and often are unsuitable to beginner programmers):

  • You ask ChatGPT to explain what a variable is in programming and how it differs from what a variable is in mathematics

  • You ask for an explanation of a Scala keyword, such as var, def, or String.

  • You ask for example programs that illustrate a generic theme, such as “how does one define a function in Scala”.

  • You use AI on some voluntary programming project of yours that is unconnected to the O1 course materials but that helps you learn programming and do well in the course.

And here are a few examples of what is forbidden:

  • Asking ChatGPT to generate code that serves as a solution to one of our programming assignments (either as is or with minor modifications).

  • Asking for code that solves even part of an assignment. (E.g., an assignment asks you to write several functions and you ask ChatGPT to generate one of them.)

  • Asking for a solution to a multiple-choice assignment.

  • Otherwise entering our course materials into an AI tool. (E.g., you ask for a short summary of one of the pages in this ebook.)

Plagiarized and AI-generated solutions will be rejected (usually not instantly but at a future time). A student who submits many such solutions will have their entire course grade invalidated. Moreover, Aalto’s School of Science will take action as per its general rules on suspected plagiarism.

The School of Science uses tools for detecting plagiarism and AI-generated content. Unfortunately, last year we had to revoke a number of O1 grades afterwards for these reasons, which was unpleasant for everyone involved. Please study honestly to avoid such issues.

The acknowledgement below is required to continue in O1. If there is anything unclear about the course policy, please bring it up with the course staff.

Feedback

Please note that this section must be completed individually. Even if you worked on this chapter with a pair, each of you should submit the form separately.

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, Kai Bukharenko, Nikolas Drosdek, Kaisa Ek, Rasmus Fyhrqvist, Joonatan Honkamaa, Antti Immonen, Jaakko Kantojärvi, Onni Komulainen, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Kaappo Raivio, Timi Seppälä, Teemu Sirkiä, Onni Tammi, 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 for this page

The idea for the question on dynamic scope came from a paper by Kathi Fisler, Shriram Krishnamurthi, and Preston Tunnell Wilson.

a drop of ink
Posting submission...