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 2.7: A Complete Scala Application

../_images/person06.png

Introduction

You have created objects. You have called their methods in the REPL, an environment that is well suited for experimentation.

However, the commands that you’ve entered in the REPL don’t form a unified whole that is stored for later use, that can be lauched again at will, that can be readily edited, and that is easily copied for someone else to run. To achieve these things, you’ll need to store your application in a file.

Let’s take a stab at doing just that.

A Traditional Program

../_images/ave_munde.png

Already the ancient Romans started their exploration of each new programming tool by creating a so-called “Hello, World” program. Such a program simply displays a message and does nothing else.

Let’s create and store a program that contains a couple of Scala commands and that we can launch as many times as we like. These print commands will serve:

println("Ave, Munde!")
println("Well, ave at thee, too!")

Create a new IntelliJ module for this experiment:

  1. Select File → New → Module. A dialog window pops up.

  2. In the top left corner of the dialog window, select New Module. You’ll see some options (name, location, etc.) on the right.

  3. Write Ave as the module’s name. But do not press Create yet. Check some other settings first, as follows.

  4. Make sure that Location is the path to the folder that stores your whole O1 project — the folder that you created at the beginning of the course and where you have all the other modules.

    • The location should not be one of the other modules (such as O1Library). It should be the overall project folder that contains the modules.

  5. Make sure that Scala is selected as the module’s language.

  6. Make sure that IntelliJ is selected as the module’s build system.

  7. Make sure that under Scala SDK you have version 3.3 selected.

  8. Make sure that Add sample code is not checked. (Otherwise, IntelliJ will generate some example code that is unsuitable for our purposes and will distract from this chapter.)

  9. Hit Create. The Ave module shows up in IntelliJ’s Project view.

Create a new Scala code file:

  1. See what’s in the Ave module that you created: you should find an empty folder named src. IntelliJ likes to create a folder with that name for storing program code. (The name is short for “source” and refers to source code.) It’s not necessary at all for programs to be stored in a folder named src, but let’s go with that here.

  2. Right-click the src folder and select New → File.

  3. You’re prompted for a file name. Enter Ave.scala

  4. The file shows up within the module and also opens up in IntelliJ’s editor. It is empty.

Write the program:

  1. Enter the two println commands in the empty file you just created. Compile the module F10.

  2. Witness: Compilation fails. The Build tab elaborates: Illegal start of toplevel definition. What’s wrong?

When we write an application, we need to specify which part of our code serves as an entry point for the app. Program execution will start with that code, which can then activate (call) other program components as needed. Our Ave application lacks such an entry point, however.

In Scala, we have two alternative ways of marking some code as an entry point, both of which are used in O1’s programs:

  • We can mark a function as the app’s main function (käynnistysfunktio). For example, our Pong application contains a function runPong, which has been marked as its main function and which runs first when you launch the game.

  • We can mark an object as the application’s app object (käynnistysolio). In a sense, an app object represents the entire application as a whole. For example, our GoodStuff application has a singleton object GoodStuff, which has been marked as its app object and whose code runs first when you launch the program.

Each of those alternatives is simple to write, and you’ll see examples of both below.

Approach #1: Writing a Main Function

The @main annotation

Let’s write a main function. Here’s a template:

@main def startMyApplication() =
  // This is a main function.
  // The commands you enter here will be executed when the app runs.

This is a perfectly ordinary function definition, except that...

... we’ve annotated it with the label @main. This marks the function as an entry point for an app.

Main functions are effectful. As customary (Chapter 2.6), we write an empty pair of brackets to mark a parameterless, effectful function.

A main function for the Ave app

A larger program may consist of dozens, hundreds, or even more functions, classes, and similar components. For our tiny greeting program, though, we need just a single function where we place the print commands; this function will also serve as the program’s main function. Let’s name the function runAveApplication, for example.

Edit Ave.scala to contain the following:

@main def runAveApplication() =
  println("Ave, Munde!")
  println("Well, ave at thee, too!")

Now you can run the program by opening the menu for the Ave file in the Project tab and selecting Run 'runAveApplication'. A still easier way is to click the Play icon play that appeared in the margin next to the word object in the code. Try it.

The output appears in IntelliJ’s Run tab at the bottom. An output area like this is called a text console (tekstikonsoli) or simply a console.

You have now written a tiny but complete Scala application.

A+ presents the exercise submission form here.

Reading Keyboard Input

The Ave app produces the same output each time it’s run, and the user has no way to affect what the program does as it runs. In constrast, most meaningful applications take in some sort of input (syöte) from their users, either directly or indirectly. Here are some examples:

  1. The program has a graphical user interface. The user can click on it to indicate what they wish the program to do.

  2. The program operates on data that it loads from files stored on the computer’s hard drive.

  3. The program interacts with the user in the text console. It pauses to wait for the user to enter lines of text as input.

We’ll get started with graphical user interfaces later in this chapter. Working with files will be discussed in Chapter 11.3. But first, let’s explore the third form of user interaction.

A text-based app in the console

Let’s write a program that works in IntelliJ’s Run tab as shown below.

Halt! Who is it?
Pechkin the Postmaster
Ave, Pechkin the Postmaster!

The highlighted middle row is an input entered by the user. The computer has paused at this point to wait for the input, continuing only when the user has pressed Enter.

The name that the user entered during this program run appears in the second output.

The readLine function

The library function readLine receives, or “reads”, a single line of user input. Calling readLine suspends the program until the input has been received. The function returns the input as a String.

The interactive program described above can be implemented with readLine:

import scala.io.StdIn.*

@main def runAveApplication() =
  println("Halt! Who is it?")
  val name = readLine()
  println("Ave, " + name + "!")

The easiest way to work with readLine is to import it first. StdIn is short for “standard input”, which here essentially means input that the user enters through a text console.

Type in (or copy–paste) the above program in IntelliJ. You can either edit the Ave object or create a new file for this second app.

Run the program. See what it prints in the console and answer the program’s request for input. (N.B. You will need to click the console before you can use the keyboard to enter input.)

It’s also good to know that you can pass a string parameter to readLine. If you do, the function first prints the string as a prompt and then reads the user’s input off the same line. Here’s an example:

val name = readLine("Halt! Who is it? ")
println("Ave, " + name + "!")

This produces interactions like the one below.

Halt! Who is it? Pechkin the Postmaster
Ave, Pechkin the Postmaster!

Mini-assignment

Write a program that asks the user for two strings and surrounds the first string with the second as shown in these example runs:

Please enter a string: llama
And a surrounding string: !?
!?llama!?
Please enter a string: cad
And a surrounding string: abra
abracadabra

Write your code in a main function named surround. Define it in a file named Surround.scala. Create the file in the Ave module next to Ave.scala in the src folder.

A+ presents the exercise submission form here.

Approach #2: Writing an App Object

As noted above, an alternative to main functions is to define an app object. You can think of an app object as representing the program as a whole; it defines what happens when the program launches. Here’s how to write our original Ave program with an app object:

object Ave extends App:
  println("Ave, Munde!")
  println("Well, ave at thee, too!")

Scala provides a type named App, which represents launchable applications.

We state that Ave is a singleton object that is a special case of the App type. (Cf. In Chapter 2.4 we had Superman as a special case of a Person type.) Our object is like any other App in that it can serve as an entry point for a program. It extends the general definition of App by...

... specifying that when this app runs, it is these specific commands that get executed.

The next assignment contains another example of an app object.

Assignment: Odds (Part 5 of 9)

Let’s write a small test program for the Odds module from Chapters 2.4 and 2.5. Our program will use keyboard input to create Odds objects, call the objects’ methods, and print a report of the return values.

Task description

Two of the files in module Odds are relevant to this assignment. Odds.scala you already know; it defines class Odds. Now take a look at OddsTest1.scala. It defines an app object, but the definition is incomplete and the app doesn’t actually use class Odds at all.

Flesh out the app object so that it produces an output that exactly matches the following example. (Of course, the user might well enter numbers other than the ones shown.)

Please enter the odds of an event as two integers on separate lines.
For instance, to enter the odds 5/1 (one in six chance of happening), write 5 and 1 on separate lines.
7
2
The odds you entered are:
In fractional format: 7/2
In decimal format: 4.5
Event probability: 0.2222222222222222
Reverse odds: 2/7
Odds of happening twice: 77/4
Please enter the size of a bet:
50.0
If successful, the bettor would claim 225.0
Please enter the odds of a second event as two integers on separate lines.
10
1
The odds of both events happening are: 97/2
The odds of one or both happening are: 70/29

Instructions and hints

  • You can start by running OddsTest1 as given. It reads in some input but doesn’t produce the correct output. The printout is missing information from the middle, and the whole bit about the second event is missing at the end.

  • In OddsTest1, add the commands to create Odds objects and call their methods.

    • Do not edit class Odds or copy any of it into OddsTest1.scala. Use the class as it is.

    • It’s important that you sequence the commands right. Pay attention to where within OddsTest1 you create new Odds objects: You can create an instance only after you’ve read the required inputs; on the other hand, you need to create it before you can call any methods on it.

  • Since OddsTest1 and Odds are in the same package, you can instantiate Odds in OddsTest1 without importing anything.

  • You’ll notice that the given code calls functions named readInt and readDouble. These two work like readLine, above, differing from it only in that they interpret user inputs as numbers. For example, readInt returns a value of type Int, not a String.

  • Hint: you can use both to compute the odds of an event occurring twice. (E.g., to roll a six twice is to roll a six and to roll another six.)

A+ presents the exercise submission form here.

Optional assignment: Eliminate redundant code with abstraction

OddsTest1 features two pieces of code that do essentially the same thing: they read in two numbers and use them as constructor parameters for a new Odds object. Avoid this unnecessary repetition with a different implementation:

  • In OddsTest1, add a method called requestOdds that takes no parameters. This effectful method reads in two integers, uses them to create an Odds instance, and returns a reference to the new object.

  • Call requestOdds (twice).

A+ presents the exercise submission form here.

An Application with a GUI

Recap: An application program operates on some problem domain. The programmer creates a model of that domain. A user interface presents the model to the end user in some form, usually as images and/or text. Many user interfaces also let the user interact with the model, affecting the model’s state. Many applications have a graphical user interface (GUI) that consists of various visual elements and is typically displayed in a separate window.

A toy example of a model

The domain of the FlappyBug game is the two-dimensional game world; in the previous chapter, we wrote the classes Bug, Game, and Obstacle, which together model this domain. Before we write a GUI for that model, let’s consider a simpler model and a GUI that displays it.

Our example model consists of just a single class, Block:

class Block(val size: Int, val location: Pos, val color: Color):
  override def toString = this.color.toString + " block at " + this.location

Below is a usage example. It works if you have the above class loaded in the REPL; the class is provided in the IntroApps module.

val model = Block(20, Pos(300, 50), Gray)model: Block = Gray block at (300.0,50.0)

Now let’s write a GUI that displays a view of a single Block object set against a solid background.

Quick recap of custom methods

Remember the Person class from the end of Chapter 2.4? And how we created a special case of it with a singleton object:

object superman extends Person("Clark"):
  def fly = "WOOSH!"

That object is a person just like any other, but additionally has a fly method. In just a moment, we’ll find a more concrete use for extending a class like that.

Displaying a GUI window: o1.View

Fortunately, programmers don’t have to build a GUI for every application from scratch, pixel by pixel. Instead, they find a software library suitable for their purposes.

There are many GUI libraries. One well-known one is called Swing; we’ll use Swing in Chapter 12.4 to work with buttons, text fields, and the like. Right now, we’ll instead use O1’s own GUI library, which is particularly useful for building small apps whose GUI consists of geometric shapes. (This library is also compatible with Swing.)

Package o1 provides a class called View. As its name suggests, this class can be used to display a view to an object that models the app’s domain. Let’s use the following Block object as our model:

val model = Block(20, Pos(300, 50), Gray)model: Block = Gray block at (300.0,50.0)

Now to create the View object. This object represents a single GUI window that displays a single object of type Block.

object viewOfBlock extends View("A non-interactive test app"):
  def makePic =
    val background = rectangle(500, 500, Black)
    val blockPic = rectangle(model.size, model.size, model.color)
    background.place(blockPic, model.location)// defined object viewOfBlock

The object is a View, but a special case of it; we mark this with extends. As a constructor parameter, we pass in a string that we wish to be shown as the window title.

In addition to being capable of everything that any View object is, our particular View object has an effect-free method makePic that forms an image of the model. The image is of type Pic, and we form it using the Pic tools familiar from earlier chapters.

The view accesses the model’s (here: the block’s) attributes to generate an image.

Nothing graphical actually showed up when we entered that command in the REPL. This is because we didn’t yet start up our GUI. Every View object has an effectful method that starts it. The following command displays our primitive GUI. Try it.

viewOfBlock.start()

Any View object is capable of drawing itself onscreen and showing the image produced by its makePic method. What we get is a window that contains the image of a block set against a black background. The window also has the customary controls for minimizing and closing it; that little bit of interactivity is automatically provided by View without us having to do anything about it.

An App with a View

Let’s combine what we’ve covered in this chapter to create a main function that fires up a GUI view.

val background = rectangle(500, 500, Black)

val block = Block(20, Pos(300, 50), Gray)

object viewOfBlock extends View("A non-interactive test app"):
  def makePic =
    val blockPic = rectangle(block.size, block.size, block.color)
    background.place(blockPic, block.location)
end viewOfBlock

@main def launchBlockApp() =
  viewOfBlock.start()

We have a model that we wish to present to the app’s user.

And a view that is capable of displaying a visual representation of the model.

When the app launches, we start up the view by calling its start method. Our main function takes care of that. Once that’s done, all the code within our main function has been executed, but our application will keep running in its separate window until the user signals otherwise.

You can find this mini-app in the IntroApps module. It is ready to run.

That sure was a boring program. Thankfully, FlappyBug will be more interesting.

Side note: makePic as an abstract method

Question: what happens if I remove makePic from the above app?

Answer: you’ll receive a compile-time error message informing you that you can’t create a View object that doesn’t have a makePic method.

Explanation: the View class has been defined so that even though it doesn’t actually implement a makePic method, it requires such an implementation to exist on all objects of type View. In more technical terms, makePic is an abstract method in class View. More on abstract methods in Chapter 7.3.

An optional practice task

If the previous example seems unclear or the next official assignment feels too difficult, you can practice on this small additional assignment.

Let’s construct a View that displays the Earth and the Moon in space. As our model, we’ll use an instance of class AreaOfSpace and the two instances of class CelestialBody that are linked to it. Here is the implementation of those classes (repeated from Chapter 2.6’s optional section where the classes were introduced):

class CelestialBody(val name: String, val radius: Double, var location: Pos):

  def diameter = this.radius * 2

  override def toString = this.name

end CelestialBody
class AreaInSpace(size: Int):

  val width  = size * 2
  val height = size

  val earth = CelestialBody("The Earth", 15.9, Pos(10,  this.height / 2))
  val moon  = CelestialBody("The Moon",   4.3, Pos(971, this.height / 2))

  override def toString = s"${this.width}-by-${this.height} area in space"

end AreaInSpace

That code is also available in o1/space/Space.scala in module IntroApps. The related file SpaceProgram.scala contains starter code for a program that displays that sort of simple model of space in a View:

val space = AreaInSpace(500)

val emptySpacePic = rectangle(space.width, space.height, Black)
val earthPic = circle(space.earth.radius * 2, MediumBlue)
val moonPic  = circle(space.moon.radius  * 2, Beige)

// Replace the question marks below with code that works.
object window extends View("A Very Simple View of Space"):

  def makePic = ???

end window

@main def launchSpaceProgram() =
  ???  // Should launch the view that the name "window" refers to.

We need a few component Pics that we’ll put together to generate a Pic of the entire model.

We define an object that extends the View class. Let’s call it window.

Your task is to fill in the missing pieces:

An implementation for makePic that places the images of the Earth and the Moon in their´correct positions against emptySpacePic. (In practice, the Earth will be visible at the left edge of the view, and the Moon near the right edge.)

A command to launch the View (displaying it onscreen).

Make the changes in module IntroApps and try running the program.

See below for the solution. You can also submit your answer for feedback.

A hint for replacing the first ??? (makePic)

We need to place two pictures onto emptySpacePic. Calling the place method a couple of times will do the trick.

You can use the space variable to access the celestial bodies and their correct positions (cf. Chapter 2.6).

Remember Chapter 2.3’s rotating-horse example? When you combine method calls on Pics, keep in mind that Pic objects are immutable and the second place operation should target the output of the first.

How to replace the first ??? (makePic)

Here are a couple of different ways to phrase a solution:

def makePic = emptySpacePic.place(earthPic, space.earth.location).place(moonPic, space.moon.location)
def makePic =
  val earthAdded = emptySpacePic.place(earthPic, space.earth.location)
  earthAdded.place(moonPic, space.moon.location)

How to replace the second ??? (launching the view)

We have a variable named window that refers to the view, and the relevant method is named start, so window.start() works.

A+ presents the exercise submission form here.

Recap and Clarifications: The View Class

There’s nothing really new in this box. If you feel like you understood the above, you can skip this elaboration on what a View is and how it behaves. You can also come back to this box while working on the assignments below, if the need arises.

The View class, start, and makePic

The two main components of an application are its internal model and a user interface. The user interface makes the model visible and typically enables the human user to somehow interact with the model (Chapter 1.2).

View is a class whose instances (various View objects) are graphical windows that serve as user interfaces. Each such View object is associated with another object, which serves as the application’s internal model and which the View window displays as graphics.

Just creating a View object does not display the user-interface window. That happens only when you start the view by calling its start method. When you do, the window not only shows up but also starts responding to user actions in the window, which is something we’ll discuss further in Week 3.

I didn’t understand how the makePic method just “ran itself”. I defined the method but never called it anywhere in my program.

Once you’ve started a view, the view draws itself onscreen using its own makePic method, which you’ve defined on it. It’s true that you don’t have to expressly invoke makePic yourself, since the View object has been programmed to take care of that on its own after it’s been started. The View window displays exactly the picture that makePic returns.

View and makePic will become more familiar in the assignments below, and even more so as Week 2 turns into Week 3.

Assignment: FlappyBug (Part 3 of 17: The Beginnings of a GUI)

../_images/module_flappybug.png

A diagram of the components in module FlappyBug. The model we’ve already discussed; now we’ll make the user interface.

Introduction

Here’s some starter code for an app object. You can also find this code in FlappyBugApp.scala.

val sky        = rectangle(ViewWidth, ViewHeight,  LightBlue)
val ground     = rectangle(ViewWidth, GroundDepth, SandyBrown)
val trunk      = rectangle(30, 250, SaddleBrown)
val foliage    = circle(200, ForestGreen)
val tree       = trunk.onto(foliage, TopCenter, Center)
val rootedTree = tree.onto(ground, BottomCenter, Pos(ViewWidth / 2, 30))
val scenery    = sky.place(rootedTree, BottomLeft, BottomLeft)

val bugPic = Pic("ladybug.png")

def makeRockPic(obstacle: Obstacle) = circle(obstacle.radius * 2, Black)

// INSERT YOUR OWN CODE BELOW.

The first bunch of commands just compose a scenic image that we intend to use as our game’s background. The variable scenery stores the end result. It isn’t necessary that you understand precisely how the background was constructed, but you can find an explanation in the optional material at the end of Chapter 2.5

Your task is to develop the given user interface further. The comment shows where your code will go. For more details, see below.

The variable bugPic stores an image that we’ll use to depict the bug in the GUI. The function makeRockPic forms a Pic of a given obstacle. You’ll need these components in the next part of the FlappyBug assignment but not just yet. For now, focus on displaying the background scenery in a View.

Reminder: please don’t forget to ask for a hint on our forums or at the labs if you’re stuck. Also, if you have trouble with this assignment, you might want to practice on the optional SpaceProgram assignment above.

Your task in detail

  1. Create a new object that will serve as the application’s domain model. A Game object is a suitable model for our FlappyBug app.

    • Also create a variable that refers to the game object. Normally, you could name the variable freely, but please use the name game here to ensure that auto-grading works properly for this assignment. (Note the lower-case g.)

    • The simple expression Game() will do to create the object (Chapter 2.6). You don’t need to pass in any constructor parameters.

  2. Create a view that depicts the Game in a GUI window. Make it a singleton object that extends the View class. The basic idea is similar to the block app, but now the domain model isn’t an instance of Block but of Game.

    • Normally, you could name the view freely, but please use the name flappyView for auto-grading purposes.

    • When you define the view, you need to pass in a constructor parameter: the window title as a string. Use the string "FlappyBug".

  3. Write a makePic method on the view. For now, just have it return the background picture (scenery). You’ll add the bug and the obstacle in just a bit.

  4. Write a main function. Name it launchFlappy and remember to follow the name with an empty pair of round brackets. The main function doesn’t need to do anything other than call start to start flappyView.

    • Note that launchFlappy is a separate function at the top level (unindented). Don’t write it inside the flappyView object, for example.

Now when your run the program, you should see a simple scenery in a window titled "FlappyBug". The result isn’t much different from what we’ve already been able to produce with the show function. Yet.

A+ presents the exercise submission form here.

Assignment: FlappyBug (Part 4 of 17: Fleshing Out the View)

Improve the makePic method. It should construct and return an image of the game’s current state. It should do so by combining the scenery, the bug’s image, and an image of an obstacle, so that the bug and the obstacle have been placed against the scenery in their correct positions.

A few hints:

  • Use the images given as scenery and bugPic.

  • At least for now, let’s depict obstacles as “rocks” that are simply solid black circles. Calling makeRockPic on an obstacle with a radius of 5 gives you a pebble; a radius of 150 yields an almighty chunk of rock.

    If you have trouble placing the rock, this may help

    makeRockPic is a function (like circle, for example, is); it’s not a variable (like bugPic, for example, is). When you call makeRockPic and pass in a reference to an Obstacle, you get a Pic of the appropriate size as a return value (a black circle that represents the obstacle).

    That is, just the expression makeRockPic alone does not produce a picture of a rock; it’s the name of a function that produces such a picture, assuming you specify the obstacle in round brackets after the function name. In case you try to add the rock with place, but get an error message of the form None of the overloaded alternatives [etc.], the first thing to check is that you’re passing the appropriate parameter to makeRockPic and then passing makeRockPic’s return value to place.

  • Given what you did in Chapter 2.6, the Game instance should have two variables: obstacle and bug. You can use them to access the game’s bug and obstacle. You will need to do so.

    • E.g., game.bug

  • You can access the positions of the game’s bug and obstacle via their pos attributes (Chapter 2.6).

    • E.g., game.bug.pos

  • Place the bug and the obstacle at their initial locations as indicated by their poses.

    • Use something in the vein of scenery.place(referenceToObstacle, positionOfObstacle).

    • Cf. BlockApp above (and the optional SpaceProgram).

    • You may want to think back to Chapter 2.3’s horse-rotating example to remember how to apply a sequence of method calls on a Pic.

Run the program. You should see both the bug and the obstacle at their initial positions. They don’t move.

They don’t move... yet.

A few comments from students who already tackled Week 3’s Flappy assignments:

I am really loving building FlappyBug bit by bit and seeing it come to life.

It was a nice feeling when Flappy Bug started moving. Little changes to code make big things happen.

It’s baffling: I’ve actually created something that moves and can be controlled.

It’s odd how you can get such a great feeling of accomplishment from making a pixel bug glide about!

A+ presents the exercise submission form here.

Optional activity: write prettier code

In the FlappyBug code we gave you, the background image is constructed with a sequence of commands that use multiple temporary variables (e.g., foliage, ground), each of which stores a partial image but has no purpose once the final scenery has been constructed. We can make our code a bit more elegant by structuring it differently. Try the following.

  1. Can you restructure that part of the code so that scenery isn’t a variable but a parameterless function that returns the scenic image? Like this:

    def scenery =
      // Place the code that
      // constructs the image here.
    

    In this solution, temporaries such as foliage become local variables as you define them within scenery’s body.

  2. How about changing def scenery to val scenery in the modified solution? Does that work? Can you tell what difference this makes, if any?

Summary of Key Points

  • You can define an entry point (i.e., “what happens first”) for a Scala application in two different ways: with a main function or an app object.

    • To define a main function, mark a function with @main. To define an app object, mark an object with extends App.

    • In O1, we’ll be using each of those alternatives. Outside O1, main functions are more common.

    • In a larger application, the main function or app object activates other program components.

  • The Scala library provides functions for reading keyboard input from the user through the text console.

  • A typical application’s program code features a domain model and a user interface that depicts and operates on the model.

  • There are various software libraries that help programmers build graphical user interfaces.

    • One GUI library is provided as part of package o1. A key component of this library is the View class, which represents GUI windows.

  • Links to the glossary: main function, app object; input, I/O; model, user interface, graphical user interface (GUI); text console.

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 appear at the ends of some chapters.

a drop of ink
Posting submission...