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 these points. The function makes use of another custom function, distance that, as you saw in the previous chapter, determines 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 implementation of another function. Here the implementation of greatestDistance calls distance (several times).

The following animation elaborates.

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.

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

As we progress towards more complex and delightful programs, it’s necessary for you to learn to draw reliable inferences about how a given piece of code works when it runs. Or about a piece of code that you wrote but that fails to work right.

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

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

Consider the aforementioned 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 indicate any two Int-valued var variables defined outside the function. 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 can be used to compute the league points of a sports team, 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 total league points of the team 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, given the parameter values "Liverpool FC", 8, 7, and 7, the function returns the string "Liverpool FC: 8/22 wins, 7/22 draws, 7/22 losses, 31 points"

Workflow

  1. 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.

  2. 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 obtain the total points of the team, 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.

  3. 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 theme of imperial units 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, given a length in meters, 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 as you implement these functions. Revisit Chapter 1.6 to review the tools available in package scala.math.

Enter the functions in week1.scala.

A+ presents the exercise submission form here.

A harder version of see-above

Find out on your own how to use pairs (pari) in Scala. Apply what you learned to solve the above problem differently.

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)

A+ presents the exercise submission form here.

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

Programming Exercise: verbalEvaluation

Let’s get back to course grades and create an effect-free function that produces a textual 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 function’s 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 out the function body. Use the buffer that’s already been defined for you as well as the overallGrade function from Chapter 1.7. If you combine these two, the solution is straightforward.

A+ presents the exercise submission form here.

Programming Exercise: doubleScore

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

Task description

Let’s consider an imaginary game where multiple players compete with each other 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 in week1.scala an effectful function 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 whatever 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 a buffer’s indices run from zero upwards (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 specifically tell you when you’re expected to take care of 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 remain with the same imaginary game. Now you’ll 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 are to be subtracted from the target player’s score. You can assume that this parameter is a positive number.

  • However, the rules of the game dictate that a player’s score can never be reduced 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 that indicates 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 score of player number two. The return value indicates that three points were successfully removed.

A look at the scores buffer confirms that the points reduction has 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, by all means go ahead and write the entire solution right now. Otherwise, we recommend approaching the solution in two stages:

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

Write a version that only 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 makes use of 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, what a function returns is determined by the last command that is executed as part of the function. A beginner’s first attempt at sketching out an algorithm for this function 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 in order to compute the return value, we need both the third parameter (the size of the penalty) and the player’s original score. Once we’ve already applied the reduction, the original score is no longer stored anywhere and there is 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 actually applying the penalty.

In order to implement this improved algorithm, you must 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 that 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

Locating 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 recognize 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, which convert 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 effortless to fix. For the beginner, too, this is typically 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 make themselves known only while 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 and 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 examples of those in Chapter 1.5 when you attempted to use excessively large and 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.2.

Note that in the REPL, the distinction between compile-time and runtime errors blurs 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 technical terms but does something other than what we intended, possibly something entirely pointless. Choosing the wrong arithmetic operation is an example of a logical error.

Some logical errors are easy to spot by looking at the code or exploring program behavior. Others are harder. In any case, locating logical errors is up to the programmer, since the computer is incapable of alerting us to them.

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 following gray-bordered box 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 greatly influenced by its type system (tyyppijärjestelmä): the general rules that govern the data types of program components and the way those types impact on how people use the language. The details or theory of type systems aren’t part of O1, but we may 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 serves as a constraint on the operations that we can apply to the value. Integers can be used in arithmetics, 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 use instructions that “go against the types”, with results that depend on context and are, in some cases, unpredictable. Type unsafety permits certain alternative approaches for solving problems but increases the likelihood of errors that slow down the programmer (even an accomplished one) and may damage the final product.

Static vs. dynamic typing

In Chapter 1.2, we pointed up that programs have a dual nature as static and dynamic, a fact that is also evident from the animations embedded in this ebook.

We can also observe this duality in type systems.

Scala is statically typed (staattisesti tyypitetty): the parts of a Scala program have types that are well defined already in the program’s static form, the program code. For instance, each Scala variable and expression has a specific data type that can be determined by looking at the code. This means, among other things, that even before we run our program, our tools can warn us about commands that are invalid, such as attempting to pass an integer as a parameter where a string is required. Static typing can also result in more efficient (faster) programs and otherwise contribute towards better programming tools. These other advantages of static typing are most prominent in larger programs that aim for high quality and reliability.

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

In a dynamically typed (dynaamisesti tyypitetty) language, the parts of program code don’t have types as such. For instance, in the Python programming language, variables don’t have types, and you can assign any kinds of values to a variable; exactly what type of value gets assigned to a variable may be determined by events that happen during a particular program run and influenced by user input. This approach 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 among themselves, 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, and the type of most or all return values must also be explicitly stated. On the other hand, in dynamically typed languages such as Python, such type annotations don’t feature at all (usually).

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, as we brought up the need to annotate each parameter variable with a type. (Chapter 1.5 foreshadowed this: we needed to declare an explicit type for the elements of an empty buffer.) We have been able to largely ignore the matter so far, because much of the static type information inherent in a Scala program can be inferred automatically. Consequently, there are many things that we can accomplish in Scala with the convenience usually associated with dynamically typed languages.

Type inference in Scala

There are certain parts of a Scala program where we must write type annotations. 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 the type inference (tyyppipäättely) capability that is built into the Scala toolkit.

For example, we have defined variables like this:

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

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

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

The shorter variants work, because the types of the variables can be readily inferred from the values that 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 do it.

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 general lesson here is that 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 the responsibility to analyze the situation and apply the right fix rests with the programmer.

Summary of Key Points

  • A function can call another function.

    • When that happens, a new frame is created on top of the call stack. The calling function’s execution is suspended as it waits for the other function to finish. Only the top frame of the call stack is actively used.

    • You can write a function that performs a subtask and then make use of that function in the implementation of another custom function.

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

  • There are a few places in Scala programs that require type annotations. Parameter variables are the most obvious example. However, Scala tools are capable of inferring the types of many program components automatically.

  • 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

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, whether it’s about general principles or some detail. We recommend that you do!

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. You do not have permission to enter our course materials into AI tools or other third-party services.

Plagiarized assignments will be rejected. Moreover, Aalto’s School of Science will take additional steps as per its general rules on suspected plagiarism.

The confirmation below is required in order 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, 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 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...