The latest instance of the course can be found at: O1: 2024
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
About This Page
Questions Answered: How do I write and store an entire Scala program? Where does a Scala application’s execution begin? How can I read input from the user’s keyboard? Now that I’ve created a conceptual model of a game world, how can I define a GUI that displays the world?
Topics: Two alternatives to launch a program: main functions and
app objects. Keyboard input with readLine
. The basics of graphical
user interfaces with o1.View
.
What Will I Do? Mostly program, guided by the text.
Rough Estimate of Workload:? Two hours.
Points Available: A75.
Related Modules: Ave (which we’ll create from scratch), Odds, IntroApps (new), FlappyBug.
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
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:
Select File → New → Module. A dialog window pops up.
In the top left corner of the dialog window, select New Module. You’ll see some options (name, location, etc.) on the right.
Write Ave as the module’s name. But do not press Create yet. Check some other settings first, as follows.
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.
Make sure that Scala is selected as the module’s language.
Make sure that IntelliJ is selected as the module’s build system.
Make sure that under Scala SDK you have version 3.3 selected.
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.)
Hit Create. The Ave module shows up in IntelliJ’s Project view.
Create a new Scala code file:
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 namedsrc
, but let’s go with that here.Right-click the
src
folder and select New → File.You’re prompted for a file name. Enter
Ave.scala
The file shows up within the module and also opens up in IntelliJ’s editor. It is empty.
Write the program:
Enter the two
println
commands in the empty file you just created. Compile the module F10.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.
... we’ve annotated it with the label @main
. This marks the
function as an entry point for an app.
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 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:
The program has a graphical user interface. The user can click on it to indicate what they wish the program to do.
The program operates on data that it loads from files stored on the computer’s hard drive.
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 createOdds
objects and call their methods.Do not edit class
Odds
or copy any of it intoOddsTest1.scala
. Use the class as it is.It’s important that you sequence the commands right. Pay attention to where within
OddsTest1
you create newOdds
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
andOdds
are in the same package, you can instantiateOdds
inOddsTest1
withoutimport
ing anything.You’ll notice that the given code calls functions named
readInt
andreadDouble
. These two work likereadLine
, above, differing from it only in that they interpret user inputs as numbers. For example,readInt
returns a value of typeInt
, not aString
.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 calledrequestOdds
that takes no parameters. This effectful method reads in two integers, uses them to create anOdds
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(model): 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 reference
to what the view will display.
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(block, "An uninteractive 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.
A small addition: we can give our app a title by passing an
additional parameter to the View
we construct.
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 a this 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 create a single object of type AreaInSpace
.
It will be our domain model.
We need a few component Pic
s 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:
1) the View
’s first constructor parameter,
which specifies the model that this particular
view will depict.
2) 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.)
3) 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.
How to replace the first ???
(the constructor parameter)
Our domain model is the instance of AreaInSpace
that we
created earlier. We have a variable space
that refers to
that object, so you can replace the first question marks with
that variable name.
A hint for replacing the second ???
(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 Pic
s, keep in mind that Pic
objects are
immutable and the second place
operation should target the
output of the first.
How to replace the second ???
(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 last ???
(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.
When you create a View
object, you need to indicate which object
will be the internal model and therefore displayed in the view.
Above, for instance, we created used a view of a block with
View(block, ...)
, and below, you’ll create a view that displays
information about a Game
object: View(myGameObject, ...)
.
(Generally speaking, View
objects need to know which object is
the model so that the view works appropriately, but you don’t have
to worry about views’ internal behavior now.)
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 start
ed 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)
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 rockPic(obstacle: Obstacle) = circle(obstacle.radius * 2, Black)
// INSERT YOUR OWN CODE BELOW.
The first bunch of commands just compose an 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 rockPic
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
Create a new object that will serve as the application’s domain model. A
Game
object is a suitable model for our FlappyBug app.The simple expression
Game()
will do to create the object (Chapter 2.6). You don’t need to pass in any constructor parameters .Also create a variable that refers to the game object. Name it as you please.
Create a view that depicts the
Game
in a GUI window. Make it a singleton object that extends theView
class. The basic idea is similar to the block app, but now the domain model isn’t an instance ofBlock
but ofGame
.Normally, you could name the view freely, but please use the name
flappyView
here to ensure that the auto-grading works properly for this assignment.When you define the view, pass in two constructor parameters: a reference to your
Game
object and the window title as a string.Put "FlappyBug" as the window title.
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.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 callstart
to startflappyView
.Note that
launchFlappy
is a separate function at the top level (unindented). Don’t write it inside theflappyView
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 by combining the scenery, the bug’s image, and an image of an obstacle so that the
bug and the obstacle have been place
d against the scenery in their correct positions.
A few hints:
Use the images given as
scenery
andbugPic
.At least for now, let’s depict obstacles as “rocks” that are simply solid black circles. Calling
rockPic
on an obstacle with a radius of 5 gives you a pebble; a radius of 150 yields an almighty chunk of rock.Given what you did in Chapter 2.6, the
Game
instance should have two variables:obstacle
andbug
. You can use them to access the game’s bug and the obstacle. You’ll need to do so.E.g.,
myGameObject.bug
You can access the positions of the game’s bug and obstacle via their
pos
attributes (Chapter 2.6).E.g.,
myGameObject.bug.pos
Place the bug and the obstacle at their initial locations as indicated by their
pos
es.Use something in the vein of
scenery.place(referenceToObstacle, positionOfObstacle)
.Cf.
BlockApp
above (and the optionalSpaceProgram
).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 cause big things.
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.
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 withinscenery
’s body.How about changing
def scenery
toval 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 withextends App
.In O1, we’ll be using each of those alternatives. Outside of 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 theView
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, Niklas Kröger, Kalle Laitinen, Teemu Lehtinen, Mikael Lenander, Ilona Ma, Jaakko Nakaza, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, 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 and Juha Sorva. Several of its key components are built upon Aleksi’s SMCL library.
The pedagogy of using O1Library for simple graphical programming (such as Pic
) is
inspired by the textbooks How to Design Programs by Flatt, Felleisen, Findler, and
Krishnamurthi and Picturing Programs by Stephen Bloch.
The course platform A+ was originally created at Aalto’s LeTech research group as a student project. The open-source project is now shepherded by the Computer Science department’s edu-tech team and hosted by the department’s IT services. Markku Riekkinen is the current lead developer; dozens of Aalto students and others have also contributed.
The A+ Courses plugin, which supports A+ and O1 in IntelliJ IDEA, is another open-source project. It 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.
This is a perfectly ordinary function definition, except that...