This course has already ended.

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 5.2: Objects Everywhere

About This Page

Questions Answered: What can I do with a String object? How do I parse string data — a star catalog, say? So, Strings are objects — what else is an object?

Topics: Several earlier topics are revisited from an object-oriented perspective. Secondary topics include operator notation, and package objects.

What Will I Do? Read and work on short assignments.

Rough Estimate of Workload:? Two or three hours?

Points Available: A60.

Related Modules: Miscellaneous, Stars.

../_images/sound_icon3.png

Notes: This chapter makes occasional use of sound, so speakers or headphones are recommended. They aren’t strictly necessary, though.

../_images/person03.png

Introduction

In Chapter 2.1, we undertook to learn object-oriented programming. Since then, you’ve created many classes and objects and used classes and objects created by others. Along the way, it has turned out that many of the basic building blocks that we use in Scala are objects and have useful methods; these objects include vectors and buffers (Chapter 4.2) and Options (Chapter 4.3).

Scala is a notably pure object-oriented language in that all values are objects and the operations associated with those values are methods.

How is that? After all, we’ve also used “standalone” functions such as max and min and written such functions ourselves. They weren’t methods of any object, right? And we have Ints and Booleans and the like, which work with operators rather than methods, right?

We’ll see.

Numbers as Objects

The data type Int represents integer values. This type, too, is in fact a class and individual integers are Int objects, even though we haven’t so far viewed them from that perspective in O1.

It’s true that Int isn’t quite a regular class. One unusual thing about this type is that you don’t need to instantiate it with new; instead, you get new values of type Int from literals. The Int class is defined as part of the core package scala, which makes it an inseparable part of the language itself. Some of the classes in this package, Int being one of them, are associated with “unusual” features such as a literal notation.

That being said, Int is a perfectly normal class in that it defines a number of methods that you can invoke on any Int object.

val number = 17number: Int = 17
number.toBinaryStringres0: String = 10001

What we did there is call toBinaryString, a method that returns a description of the integer in the binary (base 2) number system. The base-ten integer 17 happens to be 10001 in binary format.

You can even invoke a method on an integer literal:

123.toBinaryStringres1: String = 1111011

The to method

Int objects have a method called to, which returns an object that represents all the numbers between the target object (2 in the example below) and the parameter object (6).

val numbersFromTwoToSix = 2.to(6)numbersFromTwoToSix: Range.Inclusive = Range 2 to 6
numbersFromTwoToSix.toVectorres2: Vector[Int] = Vector(2, 3, 4, 5, 6)
The return value is of type Range. A range is an collection of numbers; like a vector, it is immutable.
The Range in our example contains the numbers 2, 3, 4, 5, and 6 in ascending order, which is easier to see by copying the range’s contents into a vector. (The vector takes up more space in the computer’s memory than the Range, because it stores each element separately whereas a Range stores the first and last element only.)

We’ll return to to a bit further down. And we’ll put it to good use in Chapter 5.6.

The toDouble method

Here is an example of toDouble, a frequently used method on Int objects. It returns the Double that corresponds to the Int.

val dividend = 100dividend: Int = 100
val divisor = 7divisor: Int = 7
val integerResult = dividend / divisorintegerResult: Int = 14
val morePrecisely = dividend.toDouble / divisormorePrecisely: Double = 14.285714285714286
val resultAsDouble = (dividend / divisor).toDoubleresultAsDouble: Double = 14.0

As you know, we don’t strictly need toDouble to do all that: we could have written 1.0 * dividend / divisor, for example. But toDouble says straight out what we’re after.

abs, min, etc.

Int objects also have methods for many familiar mathematical operations. For example, we can obtain a number’s absolute value or select the smaller of two integers as shown below.

-100.absres3: Int = 100
val number = 17number: Int = 17
number.min(100)res4: Int = 17
Values of the Double are objects just as Ints are. Try calling the parameterless methods floor, ceil, and round on Doubles. Which of the following claims are correct? Select all that apply.

Assuming we have a collection (such as a vector or buffer) and a variable x that refers to the collection, which of the following claims are correct? Select all that apply.

The claims concern the method to, which was introduced above, and its close relative until.

Experiment in the REPL to find out the answers. When to or until gives you a Range object, you can call the range’s toVector method to copy the contents into a vector.

Index numbering

Wikipedia has an article on Zero-based numbering.

Should indices start at 0 or 1?
My compromise of 0.5 was rejected without, I thought, proper consideration.

Stan Kelly-Bootle

Operators as Methods

So, numbers are objects and have methods. How do operators fit in?

Look at the documentation of class Int in the Scala API:

Browsing the documentation, you’ll find the methods introduced above: toDouble, to, abs, and so forth. In the same list, you’ll find methods whose symbolic names look familiar: !=, *, +, etc. (You don’t need to otherwise understand the document at this point.)

Let’s try something:

val number = 10number: Int = 10
number.+(5)res5: Int = 15
(1).+(1)res6: Int = 2
10.!=(number)res7: Boolean = false
Arithmetic and comparisons have been defined as methods on Int. We can tell an Int object to execute these methods as illustrated. The output is familiar; it’s just that now we used this somewhat more awkward notation to produce it.

Does that mean that we have an operator +, as in number + another, and, separately, a method +, as in number.+(another)?

No. What we have is a single method and two ways to write the method call. As it turns out, Scala permits us to leave out the dot and the brackets when calling a method that takes exactly one parameter. The expressions number + another and number.+(another) mean precisely the same thing. Operators are actually methods.

The concept of operator doesn’t even officially exist in Scala. (In many other languages, it does.) But it can be convenient to think of certain Scala methods — such as the arithmetic methods on numbers — as operators and omit the extra punctuation when we invoke them.

Dot notation vs. operator notation

You saw that we can omit the dot and the brackets when calling methods such as +. The same also goes for other single-parameter methods. For example, our Category class could contain either one of these expressions:

newExperience.chooseBetter(this.fave)
newExperience chooseBetter this.fave

The former is known as dot notation (pistenotaatio) and the latter as operator notation or infix operator notation (operaattorinotaatio).

Each notation has its benefits. Choosing between them is in part a matter of taste.

In this ebook, we use the operator notation sparingly. This is because dot notation works consistently for all parameter counts and because it highlights the target object and parameter expressions better, which is nice for learning.

We’ll mostly use operator notation for arithmetic, relational, and logic operators; we’ll also use it in a handful of other places where it’s particularly natural. For example, most of us would probably agree that it’s nicer to call to on an Int by writing 1 to 10 rather than 1.to(10).

In your own programs, you’re free to adopt either notation. We recommend that beginners, especially, follow the ebook’s convention. In any case, you should be aware of both notations so that you can read Scala programs written by others. Outside O1, the operator notation is somewhat more common than it is in this ebook.

“Operators” on Booleans and buffers

Logical operators (Chapter 5.1) are methods on Boolean objects.

The variable a stores the value 0 and the variable b stores 10. What value does (a.>(b).||(b.==(10))).&&(a.<(0)) evaluate to?

The buffer “operators” += and -= are methods, too. You can use either operator or dot notation to invoke them. The former is probably preferred by all and sundry.

val myBuffer = Buffer("first", "second", "third")res8: Buffer[String] = ArrayBuffer(first, second, third)
myBuffer += "fourth"myBuffer.+=("fifth")

GoodStuff revisited

In earlier chapters, you’ve seen interactive animations where GoodStuff’s objects communicate with each other via messages. In those diagrams, buffers or integers didn’t feature as objects.

As is clear by now, they too are objects: for example, an Experience object calls the > method on an Int object to find out which of two integers is bigger (so as to find out which experience is better). Communication between objects thus plays an even greater part in the program than previously discussed.

String Objects and Their Methods

At this point, it barely registers as news that String is a class, string values are its instances, and string operators are its methods.

"cat".+("fish")res9: String = catfish

A vector contains elements of some type, each at its own index. A string contains characters each at its own index. There is an obvious analogy between the two concepts, and it makes sense that strings have many of the same methods that vectors and buffers do. Strings also come with additional methods that are specifically designed for working on character data. The examples and mini-assignments below will introduce to some of the methods defined on Scala’s String objects.

String manipulation is very common across different sorts of programs. You’ll find the methods useful in many future chapters, and in this one, too. Once again, the point is not that you memorize the details of every method; with practice, you’ll learn to remember the common ones. The important thing is for you to get an overview of what sorts of tools are available to you.

The length of a string

The length method returns the number of characters in a string.

"llama".lengthres10: Int = 5

Getting a number from a string: toInt and toDouble

Let’s experiment with user inputs and String methods. We’ll write a tiny program that prompts the user for a number, then reports how many digits the number has as well as the result of another computation. The program should work like this in the text console:

Please enter an integer: 2021
Your number is 4 digits long.
Multiplying it by its length gives 8084.

Here’s a first draft of the code:

import io.StdIn._

object InputTest extends App {
  val input = readLine("Please enter an integer: ")
  val digits = input.length
  println(s"Your number is $digits digits long.")
  val multiplied = input * digits
  println(s"Multiplying it by its length gives $multiplied.")
}

The above implementation “works” as shown in the two example runs below:

Please enter an integer: 2021
Your number is 4 digits long.
Multiplying it by its length gives 2021202120212021.
Please enter an integer: laama
Your number is 5 digits long.
Multiplying it by its length gives laamalaamalaamalaamalaama.

We processed the input as a String, which is indeed necessary if we want to call its length method. However, multiplying a string with a number doesn’t give us what we want. It’s also displeasing that our program claims to treat any old string as if it was a number.

In this case, just like on countless different real-world programs, we’ll need to interpret a string that contains numerical characters as a number. For instance, given a string that consists of the characters '1',`'0', and `'5', we’d like to produce the Int value 105.

A straightforward solution is to call toInt (or toDouble, where appropriate):

val digitsInText = "105"digitsInText: String = 105
digitsInText.toIntres11: Int = 105
digitsInText.toDoubleres12: Double = 105.0

Let’s apply that to our program:

import io.StdIn._

object InputTest extends App {
  val input = readLine("Please enter an integer: ")
  val digits = input.length
  println(s"Your number is $digits digits long.")
  val multiplied = input.toInt * digits
  println(s"Multiplying it by its length gives $multiplied.")
}

The only change to the previous code is that now we interpret the numbers as an Int for the multiplication to work.

Which of the following claims about the above program are correct?

A safer choice: toIntOption and toDoubleOption

The previous program suffered from the common problem that invalid user input can lead to missing information. Although some small programs for personal use don’t always need to cover such exceptional situations, many other programs do.

You already know that the Option type is one way to represent missing information. One easy way to produce an Option is to call toIntOption rather than toInt; there is also toDoubleOption and some other similar methods.

"100".toIntOptionres13: Option[Int] = Some(100)
"a hundred".toIntOptionres14: Option[Int] = None
"100.99".toDoubleOptionres15: Option[Double] = Some(100.99)

Mini-assignment: toIntOption

Take out the InputTest program o1.inputs in the Miscellaneous module.

Merely replacing the toInt call with toIntOption won’t make the code work. On the contrary, that simple modification makes the code unrunnable. (Why?)

You’ll need to deal separately with the case where the input is valid and the case where it’s invalid. Edit the program so that it works as shown in the two example runs below. (Hint: use match to cover each case.)

Please enter an integer: 105
Your number is 3 digits long.
Multiplying it by its length gives 315.
Please enter an integer: ten
That is not a valid input. Sorry!

A+ presents the exercise submission form here.

Removing whitespace: trim

The trim method is also handy when you need to process data that you’ve obtained from an external source. It returns a string in which space characters and other *whitespace* have been removed from both ends.

var text = "  hello there "text: String = "  hello there "
println("The text is: " + text + ".")The text is:   hello there .
println("The text is: " + text.trim + ".")The text is: hello there.
The original string has some whitespace at both ends. The REPL highlights this fact by printing quotation marks around the string. The quotation marks in the output are not actually part of the string itself.

If there is no leading or trailing whitespace, trim returns an untouched string:

text = "poodle"text: String = poodle
println("The text is: " + text.trim + ".")The text is: poodle.

trim can be summarized as “removing empty characters from a string”. To be more specific, however, the method does not actually change the original string but produces a new string that contains the same characters as the original except for the omitted whitespace. All methods on String objects are effect-free, trim included. Every String object is immutable.

Picking out a character: apply and lift

The apply method takes an index as a parameter and retrieves the corresponding character from the string. The numbering starts at zero, so the following code checks the fourth and fifth letters in "llama":

"llama".apply(3)res16: Char = m
"llama".apply(4)res17: Char = a
Note the return type. In Scala, individual characters are represented by the Char class. A String is a sequence of zero or more characters; a Char is exactly one character.

There’s also a more succinct way to access a character at an index. Try "llama"(3), for instance.

And there is a safer way: the lift method you know from other collections.

"llama".lift(3)res18: Option[Char] = Some(m)
"llama".lift(4) res12: Option[Char] = Some(a)
"llama".lift(123)res19: Option[Char] = None

splitting a string

The split method divides a string into parts at the given separator. In this example, the separator is a space:

val quotation = "A class is where we teach objects how to behave. ---Richard Pattis"quotation: String =  A class is where we teach objects how to behave. ---Richard Pattis
val words = quotation.split(" ")words: Array[String] = Array(A, class, is, where, we, teach, objects, how, to, behave., ---Richard, Pattis)
words(1)res20: String = class
split returns an Array, a collection of elements similar to a vector or a buffer.
You can use the array as you’ve used a vector. The differences between these collection types are discussed in Chapter 11.1.

Any string can serve as a separator:

quotation.split("ch")res21: Array[String] = Array(A class is where we tea, " objects how to behave. ---Ri", ard Pattis)

Letter size: toUpperCase and toLowerCase

val sevenNationArmy =
  "[29]<<e--------e--g--. e--. d--. c-----------<h----------->e--------e--g--. e--. d--. c---d---c---<h-----------/360"sevenNationArmy: String = [29]<<e--------e--g--. e--. d--. c-----------<h----------->e--------e--g--. e--. d--. c---
d---c---<h-----------/360
play(sevenNationArmy)

That needs to be played louder.

The function play plays upper-case notes louder than lower-case ones. There is an easy way to produce upper-case letters:

play(sevenNationArmy.toUpperCase)

Here’s an additional example of the method and its sibling toLowerCase:

"Little BIG Man".toUpperCaseres22: String = LITTLE BIG MAN
"Little BIG Man".toLowerCaseres23: String = little big man

Comparing strings: compareTo

You already saw in Chapter 3.3 that strings have comparison operators such as <. There’s also a compareTo method that comes in handy sometimes:

"my dad".compareTo("your dad")

The method compares strings based on where they are in the “alphabetical” order defined by the Unicode standard. Which of the following claims are correct? Select all that apply.

Searching for characters: indexOf and contains

The methods indexOf and contains work much like the corresponding methods on vectors and buffers (from Chapter 4.2). You can use them to find out if a given substring occurs within a longer string:

"fragrant".contains("grant")res24: Boolean = true
"fragrant".contains("llama")res25: Boolean = false
"fragrant".indexOf("grant")res26: Int = 3
"fragrant".indexOf("llama")res27: Int = -1

take, drop, and company

take, drop, head, and tail are available on strings, too. These methods and their variants are analogous to the collection methods from Chapter 4.2. Here are a few examples:

val fromTheLeft = "fragrant".take(4)fromTheLeft: String = frag
val fromTheRight = "fragrant".drop(5)fromTheRight: String = ant
"fragrant".take(0)res28: String = ""
"fragrant".take(100)res29: String = fragrant
"fragrant".takeRight(5)res30: String = grant
"fragrant".dropRight(5)res31: String = fra
val first = "fragrant".headfirst: Char = f
val rest = "fragrant".tailrest: String = ragrant
val firstIfItExists = "fragrant".headOptionfirstIfItExists: Option[Char] = Some(f)
val firstOfEmptyString = "fragrant".drop(10).headOptionfirstOfEmptyString: Option[Char] = None

Strings have two methods named substring, one of which takes a single integer parameter while the other takes two.

Experiment with the substring methods in the REPL. Use different strings. Given your experiments, which of the following claims seem correct? In each of the claims, assume that x is a variable of type String and that it refers to a string that contains a minimum of four characters.

Clarification: where the following claims say “the same value”, this means that the values must be exactly identical: both the content and the data type must match.

Optional assignment: string insertion

In misc.scala of module Miscellaneous, write an insert function that adds a string into a specified location within a target string. It should work as illustrated below.

val target = "viking"target: String = viking"
insert("olinma", target, 2)res32: String = violinmaking
insert("!!!", target, 0)res33: String = !!!viking
insert("!!!", target, 100)res34: String = viking!!!
insert("!!!", target, -2)res35: String = !!!viking

A+ presents the exercise submission form here.

Speaking of strings: special characters

Chapter 4.2 mentioned in passing that you can include a newline character (a line break) in a string by writing \n. There’s a similar notation for a number of other special characters. Here are a few of them:

Character Written in literals as
newline \n
tabulator \t
double quotation mark \"
backslash \\

For example:

println("When I beheld him in the " +
        "desert vast,\n\"Have pity " +
        "on me,\" unto him I cried,\n" +
        "\"Whiche'er thou art, " +
        "shade or real man!\"")

That produces the following output:

When I beheld him in the desert vast,
"Have pity on me," unto him I cried,
"Whiche'er thou art, shade or real man!"

As you can see, the line breaks appear where the newline characters are.

An alternative is to mark the string’s beginning and end with three double-quote characters. This tells Scala to interpret everything in between, special characters and all, as individual characters, so that there’s no need to use the backslash as an “escape”. The following code produces the same output as the one above.

println("""When I beheld him in the desert vast,
"Have pity on me," unto him I cried,
"Whiche'er thou art, shade or real man!"""")

Assignment: tempo

Introduction

The example below uses a function that takes in similar string of music as play does and returns the tempo of that music as an integer.

tempo("cccedddfeeddc---/180")res36: Int = 180
tempo(s"""[72]${" "*96}d${"-"*39}e---f---d${"-"*39}e---f---d${"-"*15}e---f---d${"-"*15}e---f---
f#-----------g${"-"*17}&[62]${" "*104}(>c<afd)--(>c<afd)--------(afdc)--(>c<afd)-------(afdc)
${"-"*17}       (>c<abfd)--(>c<abfd)--------(abfdc)--(>c<abfd)-------(abfdc)${"-"*17}      (hbgfd)
${"-"*17}(hbg>fd<)${"-"*22}(>ce<hbg)---- (>d<hbge)---------- (>db<hbge)---------- (>c<hbg#e)-----
&[29]${" "*96}<<<${"c-----------"*11}cb-----------<${"hb-----------"*3}hb-----&P:a-----------
${"a--------a--a-----------"*11}/480""")res37: Int = 480

The function completely ignores the string’s actual musical content; it just extracts the tempo from the end. If there’s no tempo recorded in the string, the function returns the integer 120:

tempo("cccedddfeeddc---")res38: Int = 120

Task description

Implement tempo in misc.scala of module Miscellaneous.

Instructions and hints

  • The function must return an Int, not a String that contains the digits. The function needs to divide the string, select one of the parts, and interpret it as an integer.
  • There are a great many ways to implement the function. Just the String methods introduced in this chapter suggest multiple solutions. Can you find more than one?
  • You may assume that the given string has at most a single slash character /, no more. You may also assume that in case there is a slash in the string, it’s followed by one or more digits and nothing else. In other words, you don’t have to worry about invalid inputs.

A+ presents the exercise submission form here.

A better representation for songs?

In this ebook, you’ve seen some fairly long strings being passed as parameters to play. Writing and editing strings such as these is laborious and attracts bugs. Fortunately, you don’t have to.

If you want, you can reflect on what functions you might create to help you notate songs in O1’s string format.

You may also: 1) consider what other format we could use to represent musical notes in a program; 2) envision an application where an end user can easily write and save songs; and 3) find out what solutions are already out there.

Assignment: Star Maps (Part 3 of 4: Star Catalogues)

We concluded Chapter 4.4’s Stars assignment with this observation:

We can now display individual stars in a star map, but it would take a lot of manual work to display a map with more than a sprinkling of stars. It would be much more convenient if we could load star data into our program from a repository of some sort.

The folders test and northern of the Stars module each contain files named stars.csv. Take a look.

  • The file under test contains a semicolon-delimited description of a few imaginary stars. You’ll learn what each number means in just a moment.
  • The file under northern contains a much longer list of actual stars (originally from the VizieR service).

For now, feel free to ignore the other files under northern.

We’ll now improve our program so that it 1) reads in the star data from the files as strings, 2) creates Star objects to represent the information encoded in those strings, and 3) displays all the stars as graphics. The last of the three subproblems you already solved in Chapter 4.4. The second subproblem of interpreting the string data you’ll solve now. The first subproblem of actually handling the file has been solved for you in the given code. (More on files later in Chapter 11.3.)

Task description

Run StarryApp. Be disappointed by its flat black output. Exit the app.

Actually, the app is very close to working nicely, but a crucial method of the SkyFiles singleton object is missing. That method, parseStarInfo, does what was just discussed: takes in a string that describes a star (as the lines of stars.csv do) and creates a corresponding Star-object.

Read the documentation for parseStarInfo. It explains what each part of the input string is expected to contain and which of those parts are significant for your task. Then implement the method.

Instructions and hints

  • The method has been documented as part of the SkyFiles object in package o1.stars.io.
  • The folder that the app loads its star data from is named at the top of StarryApp. As given, the app uses the test folder, which is a good choice until you feel confident that your method works. Later, you can change the folder to northern to produce a more impressive star map.
  • You do not have to consider what happens if the input string doesn’t consist of six or seven semicolon-separated parts or otherwise fails to adhere to the specification. Assume that the given data string is valid.
    • The program will crash with a runtime error if the selected stars.csv contains invalid data. This would obviously be unacceptable in a proper real-world application but it will do for present purposes.
  • You can find helpful methods in this very chapter.
  • Some of the star data (the z coordinate and one of the ID numbers) are irrelevant to this assignment.
../_images/module_stars_noconstellations.png

A diagram of the Stars module. In this chapter, you only implement one method in SkyFiles.

A+ presents the exercise submission form here.

Bonus Material: Package Objects and import Statements

The remaining topics in this chapter aren’t particularly important for O1 or indeed for learning to program in general, but they can be nice to know for programming in Scala.

Objects as packages

What about the “separate functions” that you have used and written, such as sqrt (Chapter 1.6) or toMeters (Chapter 1.7) or tempo (just now)? Those functions weren’t attached to any object, so do they count as object-oriented programming?

In a way, yes.

Technically, even those functions were methods, even though it didn’t seem like it. This fact has its basis in two things.

One: in Scala, we can import methods from an object. For example, import mysingleton._ imports all the methods of the mysingleton object. That means that we can then omit the dot and the object’s name as we call the method, and our method calls won’t look like method calls.

Two: we can define a singleton object that is meant to be imported from and that contains an assortment of methods that are more or less related to each other. Such an object is termed a package object (pakkausolio).

In this chapter, for example, you defined some functions in an object defined as follows.

package o1
object misc {

  // Your code went here.

}

What you did, then, was define the functions as methods of a package object named misc. After importing those methods with import o1.misc._ (which our REPL does automatically), you could invoke them as if no target object was involved at all.

The Scala API features various package objects. One of them is math, which defines sqrt and other familiar functions.

So, in a technical sense, these functions, too, are methods on singleton objects. This is consistent with Scala’s pure object-oriented design.

However, in practice, we commonly don’t think about the methods on package objects as being methods. In a sense, that’s exactly what a package object is: an object whose object-ness we can “overlook”.

One of the changes in version 3 of Scala, which has just come out in 2020 (but we don’t use yet in O1), is that package objects are superseded by a different solution.

On package objects and object-orientation

If we were to design an entire piece of software so that we use only package objects and their methods, our design won’t capture the spirit of object-oriented programming. When using Scala in an object-oriented fashion, it’s customary to make limited use of methods in package objects.

For example, the package object scala.math is more the exception than the rule; most of the Scala API relies on classes that we instantiate. And indeed even many of that package object’s contents are also available as methods on instances: for example, you can get the absolute value of a number either “function-style” with abs(a) or “object-style” with a.abs. Take your pick.

println, readLine, and package objects

The familiar println function is actually a method on a singleton object named Predef. This one particular object has an elevated status in Scala: its methods can be called in any Scala program without an import and without an explicit reference to the object.

As for readLine, you learned to use it in Chapter 2.7 by first importing scala.io.StdIn._. As you did that, you essentially used a singleton object StdIn as a package object.

importing just about anywhere

In the ebook’s examples, we have generally placed any import statements at the top of the Scala file. This is a common practice. However, Scala also lets you import locally within a particular class or object or even an individual method.

import myPackage1._

class X {
  import myPackage2._

  def myMethodA = {
    // Here, you can use myPackage1 and myPackage2.
  }

  def myMethodB = {
    import myPackage3._
    // Here, you can use myPackage1, myPackage2, and myPackage3.
  }

}

class Y {
  // Here, you can use only myPackage1.
}

Such local imports sometimes make code easier to read.

importing from an instance

As noted above, you can use a singleton object like a package and import its methods. As a matter of fact, you can even do the same to an instance of a class, if you’re so minded.

class Human(val name: String) {
  val isMortal = true
  def greeting = "Hi, I'm " + this.name
}defined class Human
val soccy = new Human("Socrates")soccy: Human = Human@1bd0b5e
import soccy._import soccy._
greetingres39: String = Hi, I'm Socrates

The last command issued above is actually a shorthand for soccy.greeting.

However, in most cases importing from instances like this is liable to make your code harder to read.

Bonus Material: Rich vs. Thin Interfaces

Many similar methods — good or bad?

Here are a few student remarks on many methods on strings and collections that have come up in this and earlier chapters:

Scala seems to have a really handy set of methods for string manipulation. It’s great that [the Scala API] comes with so many methods built in.
This chapter revealed more about Scala but also made me wonder why there need to be two ways to do the same thing. It seems like that mostly creates opportunities for confusion.
Scala’s basic objects seem to have a lot methods that are fairly unnecessary i.e. do the same thing under different names.
Compared to a lot of other languages, Scala seems to have a very comprehensive set of string-handling methods.

Indeed it is not rare that a class has several methods that do similar things and that aren’t all strictly necessary. This is the case with many of Scala’s core classes, too.

In other words, these classes don’t offer their users a thin interface but a rich one. This theme is discussed in Programming in Scala (which is one of the books listed on our resources page):

Thin versus rich interfaces represents a commonly faced trade-off in object-oriented design. The trade-off is between the implementers and the clients of an interface. A rich interface has many methods, which make it convenient for the caller. Clients can pick a method that exactly matches the functionality they need. A thin interface, on the other hand, has fewer methods, and thus is easier on the implementers. Clients calling into a thin interface, however, have to write more code. Given the smaller selection of methods to call, they may have to choose a less than perfect match for their needs and write extra code to use it.

Odersky, Spoon & Venners

It’s true that the rich interface’s user may initially feel a bit overwhelmed with the wealth of options at their disposal, but usually the riches pay off sooner rather than later.

Summary of Key Points

  • As an object-oriented language, Scala is pure: all values are objects.
  • Int, Double, and String, for example, are classes and values of those types are objects. These objects have a variety of frequently useful methods.
  • The so-called operators are actually methods. In Scala, there are two ways to call a method: dot notation and operator notation.
  • Links to the glossary: object-oriented programming; dot notation, operator notation; package object.

Does that go for other languages, too?

Scala is certainly not the only language where “everything is an object”. However, it does differ in this respect from some other object-oriented languages in common use. In the Java programming language, for example, objects and classes are used for a variety of data, but some fundamental data such integers and Booleans are instead represented as so-called primitive types. Scala’s all-encompassing object system eliminates special cases and gives the language a more consistent feel.

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 that has 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, Joonatan Honkamaa, Jaakko Kantojärvi, Niklas Kröger, Teemu Lehtinen, Strasdosky Otewa, Timi Seppälä, Teemu Sirkiä, 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 was created by Nikolai Denissov, Olli Kiljunen, Nikolas Drosdek, Styliani Tsovou, Jaakko Närhi, and Paweł Stróżański with input from Juha Sorva, Otto Seppälä, Arto Hellas, and others.

For O1’s current teaching staff, please see Chapter 1.1.

Additional credits for this page

The Stars program is an adaptation of a programming assignment by Karen Reid. It uses star data from VizieR.

This chapter does injustice to music by Glenn Miller and The White Stripes. Thank you and sorry.

The guy in the desert vast was Dante Alighieri.

a drop of ink
Posting submission...