14 Languages - Scala
2015-06-30
The forth language - Scala
Introduction
Scala is a strongly and statically typed hybrid language. It manages to be both an object-oriented language and a functional language at the same time. It is based on Java.
Scala runs on the JVM and, thus, can use Java libraries directly.
Executing Scala programs
Scala programs can be executed by the scala
interpreter bundled with the Scala Development Kit.
When starting up the scala
interpreter, the following prompt pops up scala>
At that prompt, Scala programs can be entered. If the interpreter seems to hang after some piece of a Scala program has been entered, just have a little bit of patience. The interpreter is quite slow.
The second way that Scala programs can be executed is by supplying the name of a file containing a Scala program to the scala
executable when running it from the command line. E.g.
lars@lars-lilla:$ scala helloWorld.scala
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Hello World!
lars@lars-lilla:$
Scala programs can also be compiled to Java bytecode and executed by the JVM. The command to compile a Scala program is logically called scalac
. It produces one or more .class
files. The class
file containing the main
method can then be executed by the scala
program.
lars@lars-lilla:$ scalac HelloWorld.scala
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
lars@lars-lilla:$ scala -cp . HelloWorld
Picked up JAVA_TOOL_OPTIONS: -javaagent:/usr/share/java/jayatanaag.jar
Hello, World!
As you can see, I had to supply the -cp .
arg to scala
to tell it to look for classes in the current dir (as . is not on the CLASSPATH
).
Basic Syntax
Semicolons
Semicolons are optional.
Comments
Comments in Scala are created using the //
, /* */
or /** */
styles. The last form is called scaladoc and is similar to javadoc in Java.
Hello World!
Hello World in Scala can look like this:
println("Hello World!")
Listing 1: Hello World in Scala.
Variables
A variable is defined using the val
keyword or the var
keyword. The difference between them is that variables defined using val
are immutable, variables defined using var
are not.
Example:
scala> val i = 1
i: Int = 1
scala> var j = 20
j: Int = 20
scala> j = 30
j: Int = 30
scala> i = 2
<console>:8: error: reassignment to val
i = 2
^
Scala tries, if possible, to guess which type a variable has. This is done at compile time and is called type inference. You can explicitly declare the type of a variable by appending it after the variable name separated by a comma. E.g. val b : Int = 2
Arrays
Arrays are costructed using the following syntax: val arrayOne : Array[String] = new Array[String](3)
The compiler can most often infer the type of the array, which means that the example above could have been written like this: val arrayOne = new Array[String](3)
It is also possible to initialize the elements at construction time: val arrayTwo = Array("One", "Two", "Three")
To retrieve an element of an array the index at which the desired element resides is specified in regular parenthesis:
val arrayTwo = Array("One", "Two", "Three")
println("The second element of the array is: " + arrayThree(1))
...
The second element of the array is: Two
Listing 2: Accessing an element in an array.
The same syntax is used when assigning an individual element of an array:
val arrayThree = Array("One", "Two", "Three")
arrayThree(1) = "Four"
println("The second element of the array is now: " + arrayThree(1))
...
The second element of the array is now: Four
Listing 3: Assigning to an element in an array.
Note that we could assign to the index in the array despite the variable pointing to the array being a val
. It is only forbidden to reassign the variable pointing to the array, not updating the array itself.
In Scala it is also possible to create multi-dimensional arrays using the ofDim()
method from the Array
class.
import Array._
val multiDimArray = ofDim[String](2, 3)
multiDimArray(0)(0) = "a"
multiDimArray(0)(1) = "b"
multiDimArray(1)(0) = "c"
println("Accessing an element in the multi-dimenstional array: " + multiDimArray(0)(1)
Listing 4: Multi-dimensional arrays.
Tuples
Tuples are created by enclosing a comma separated list of values in parenthesis: var tpl = ("Hello", "World")
The elements of a tuple can be accessed using the ._x
syntax, where x is a number between 1 and the length of the tuple. An example:
var tpl = ("Hello", "World")
println("The first item of the tuple is: " + tpl._1)
println("The second item of the tuple is: " + tpl._2)
...
The first item of the tuple is: Hello
The second item of the tuple is: World
Listing 5: Using tuples.
It is an error to assign to a to a tuple once it has be created or to try to access an index greater than the length of the tuple. Such errors are caught at compile time.
Multivariable assignment in Scala can be done using tuples:
val (a, b) = ("A", "B")
println("a = " + a)
println("b = " + b)
...
a = A
b = B
Listing 6: Multivariable assignment.
Lists
A List
is an immutable implementation of a linked list. It it meant to to be operated on only at the head the end. It is possible to access the elements in the middle of the list, but not reassign them.
Lists can, like tuples be created in a number of ways. One way if to use the List
constructor: val aList : List[String] = List("a", "b", "c")
. Another way is to use cd cons construction ::
. Cons separates the head value of a list from the tail of the list, it but can also be used to create a list. Like this: val integerList = 1 :: (2 :: (3 :: (4::Nil)))
. Note that Nil
is equal to an empty list.
To access an element in the firs element in a list, the head
method can be used:
println("The first item of the list is: " + integerList.head)
...
The first item of the list is: 1
Listing 7: Using head to access the first element in a list.
Retrieval of an element in a specific posistion in the list can be performed using the syntax list(position)
where 0 represents the first element in the list, 1 the second and so forth. E.g.
println("The first item of the list is: " + integerList(0))
println("The second item of the list is: " + integerList(1))
println("The third item of the list is: " + integerList(2))
println("The fourth item of the list is: " + integerList(3))
...
The first item of the list is: 1
The second item of the list is: 2
The third item of the list is: 3
The fourth item of the list is: 4
Listing 8: Accessing elements in all positions in a list using ().
As lists are immutable, you cannot add or remove anything from it. It it however possible to create a new list that is based on the old list but has an element added at the front or at the end. The ::
operator can be used to add an element the the head of a list and the :+
operator can be used to add to the end.
val integerList2 = 0 :: integerList
println("The first item of the second list is: " + integerList2(0))
val integerList3 = integerList :+ 5
println("The last item of the third list is: " + integerList3(4))
...
The first item of the second list is: 0
The last item of the third list is: 5
Listing 9: Forming new lists by appending or prepending elements to an old list.
Note that this creation of new lists is not as inefficient as it seems. This is so because Scala shares memory between the original list and the new lists.
To extract a list containing all elements but the first of an original list, the tail
method can be used:
println("All but the first element of the third list: " + integerList3.tail)
...
All but the first element of the third list: List(2, 3, 4, 5)
Listing 10: Using tail
The isEmpty()
method checks if a list is empty or not.
println("Is integerList3 empty? " + integerList3.isEmpty)
println("Is a List() empty? " + List().isEmpty)
...
Is integerList3 empty? false
Is a List() empty? true
ArrayBuffer and ListBuffer
Sometimes it is desired to work on a mutable collection. Both ArrayBuffer
and ListBuffer
are such collections. Both live in the scala.collection.mutable
package. The difference between them is that ArrayBuffer
is backed by an array but ListBuffer
is backed by a list. The difference is mainly important if you want to convert your collection into an array or a list.
An example:
import scala.collection.mutable.ArrayBuffer
val arrayBuffer1 = ArrayBuffer.empty[String]
// += can be used to append an element to the end of the buffer.
arrayBuffer1 += "a"
println("First element: " + arrayBuffer1(0))
// As can append().
arrayBuffer1.append("b")
println("* First element: " + arrayBuffer1(0))
println("* Second element: " + arrayBuffer1(1))
// Modify the last element.
arrayBuffer1(1) = "B"
println("** First element: " + arrayBuffer1(0))
println("** Second element: " + arrayBuffer1(1))
// Remove the first element.
arrayBuffer1.remove(0)
println("*** First element: " + arrayBuffer1(0))
...
First element: a
* First element: a
* Second element: b
** First element: a
** Second element: B
*** First element: B
Ranges
Ranges can be created using the until
or to
keywords. The difference between them is that to
is inclusive and to
is exclusive. A step can be specified using the by
keyword.
val rangeOne = 1 until 5
val rangeTwo = 1 to 5
val rangeThree = (1 to 5) by 2
val rangeFour = (5 to 1) by -2
Listing 11: Examples of ranges.
Maps
Maps can be created using either the Map
keyword or using the regular new Map()
syntax. Scala uses ->
to specify a key-value-pair and () to access the elements. Here is a small example program demonstrating the most basic Map operations:
// Create an (immutable) Map using the Map keyword.
val aMap = Map("a"->"the first letter", 42 -> "Meaning of life")
println(aMap)
// Create a (mutable) Map by importing HashMap and creating a new instance of it.
import scala.collection.mutable.HashMap
val bMap = new HashMap[Int, String]
bMap += 1 -> "First"
bMap += 2 -> "Second"
println(bMap)
// Extract value for a specific key.
println("The value of the 1 key is: " + bMap(1))
// Change value for key.
println("Changing value of for the 2 key to Seven")
bMap(2) = "Seven"
println("The value of the 2 key is now: " + bMap(2))
...
Map(a -> the first letter, 42 -> Meaning of life)
Map(2 -> Second, 1 -> First)
The value of the 1 key is: First
Changing value of for the 2 key to Seven
The value of the 2 key is now: Seven
Listing 12: Creating maps, adding elements and retrieving values.
Sets
As with lists, there are both mutable and immutable versions of Sets. By default, the immutable variant is imported into Scala programs (by means of the Scala package object, see below). If you want to have a mutable set, use import scala.collection.mutable.Set
.
The Set
class supports all kinds of operations, e.g. union, intersection and difference. A small example program exercising a (mutable) Set
is shown below.
import scala.collection.mutable.Set
println("------------- aSet ------------")
val aSet = Set("one", "two")
println(aSet)
println("---------Add stuff to aSet---------------")
aSet += "three"
println(aSet)
println("------------- bSet ------------")
val bSet = Set("two", "four")
println(bSet)
println("---------- UNION ---------------")
var unionSet = aSet ++ bSet
println(unionSet)
println("---------- DIFFERENCE ---------------")
var differenceSet = aSet -- bSet
println(differenceSet)
...
------------- aSet ------------
Set(two, one)
---------Add stuff to aSet---------------
Set(three, two, one)
------------- bSet ------------
Set(four, two)
---------- UNION ---------------
Set(four, three, two, one)
---------- DIFFERENCE ---------------
Set(three, one)
Listing 13: Set example program.
If-clauses
An if-clause in Scala is written like this:
var i = 42
if (i < 0) {
println("Less than 0")
} else if (i <= 100) {
println("Between 0 and 100.")
} else {
println("Greater than 100.")
}
Listing 14: An if-clause.
Note that if clauses really are if expressions in Scala. Every if
will return a single value. As an example, take a look at the following method that switches between the two players in a Tic Tac Toe game:
class Game {
var activePlayer = PLAYER_1
...
def nextPlayer() : PlayerType = {
return if (activePlayer == PLAYER_1) PLAYER_2 else PLAYER_1
}
Listing 15: An if-clause used as an expression.
The last statement in the selected branch in the if - else if - else
clause becomes the value of the if-expression.
Loops
A while loop can look like this:
var i = 0
while (i < 5) {
println(i)
i += 1
}
Listing 16: A while loop.
A for loop has the following syntax:
for (i <- 0 until 4) {
println("This is iteration: " + i)
}
Listing 17: A for loop.
Scala also supports for-each-loops. Example of one such:
val anArray : Array[String] = Array("One", "Two", "Three")
anArray.foreach { item =>
println(item)
}
Listing 18: A foreach loop.
Functions
A function is defined using the def
keyword. If the function does not take any parameters, the parenthesis can be omitted. If they are omitted, however, you must be careful not to include them when the function is called. If you do include them when calling the function, the compiler will complain. A rather peculiar thing is that if you include the parenthesis when defining a function that does not take any parameters, then you must include them when calling the function. That does seem logical. I think. At any rate, I'm sure there's a good explanation. I'm sure of it. Almost.
An example of two methods that does not take any arguments:
// Define two methods that don't take any arguments.
def printGreeting {
println("Hello, friend!")
}
def printGreeting2() {
println("Hello, friend2!")
}
// Call them.
printGreeting
printGreeting2()
Listing 19: Two no-arg functions.
The type of a parameter is specified after the name: anIntegerParam : Int
. If the function return a value, the type of the returned value is specified after the parameter list:
def aFunctionReturningSomething() : Int = return 42
Listing 20: A function returning a value.
The main()
method and accessing command line args
A main
method of a Scala program is created like this:
def main(args : Array[String]) {
//...
}
Listing 21: A main-method.
The command line arguments can be accessed from the args
array. Element 0 will contain the first argument, element 1 the second one and so on.
package testPackage
object MainTest {
def main(args : Array[String]) {
for (j <- 0 until args.length) {
println("Argument " + j + " is " + args(j))
}
}
}
Listing 22: Accessing the command line args.
The match
expression
Scala has a construct similar to switch
in Java and C, but a little bit more powerful. To match a variable against a list of fixed values you write
aVariable match {
case "firstCase" => doStuffForCaseA
case "secondCase" => doStuffForCaseB
case _ => doStuffForDefaultCase
}
Listing 23: Syntax of the match
expression.
We see that the underscore character is used as a wildcard matching everything that the other cases didn't catch.
As an example, let's write a program that matches the first command line argument against a number of fixed strings and prints an appropriate message.
def main(args : Array[String]) {
args(0) match {
case "hi" => println("Hi!, Hi!")
case "hopp" => println("Hopp, hopp")
case _ => println("A good day to you took, Sir!")
}
}
...
lars@lars-lilla:~/programmering/14languages/scala/day3$ scala -cp . matchTest.MatchTest hi
Hi!, Hi!
lars@lars-lilla:~/programmering/14languages/scala/day3$ scala -cp . matchTest.MatchTest hopp
Hopp, hopp
lars@lars-lilla:~/programmering/14languages/scala/day3$ scala -cp . matchTest.MatchTest Good day!
A good day to you took, Sir!
Listing 24: Using match
to match input arg against a set of strings.
Classes and Objects
The class
keyword is used to define a class. If the class should contain attributes only (and thus be a Data Transfer Object, Value Object or whatever you wish to call that particular design pattern), you don't have to specify a body when defining the class. The name and types of the attributes is sufficient.
package dtos
class PairOfStrings(val first : String, val second : String)
Listing 25: A class without a body.
The class can be used like this:
package testPackage
import dtos.PairOfStrings
object PairTest {
def main(args : Array[String]) {
val pair = new PairOfStrings("one", "two")
println("The first item in the pair is: " + pair.first)
println("The second item in the pair is: " + pair.second)
}
}
...
The first item in the pair is: one
The second item in the pair is: two
Listing 26: Program using the body-less class.
Now to something that I find a little peculiar about Scala: constructors. If you want to include a constructor into a class, which you in my opinion want to do fairly often, you just type expressions and statements after the row containing the class
keyword. As an example, consider this simple class modeling a person:
package dtos
class Person(firstName : String, lastName : String) {
val fullName = firstName + " " + lastName
println("Inside the primary constructor.")
def getFirstName() = firstName
def getLastName() = lastName
def printName() {
println(fullName)
}
}
Listing 27: Simple person class.
We see that we defined a field called fullName
inside the constructor. We also print out the full name of the Person on std out. Both of these lines are part of the constructor. In fact, also the method definitions that come after are also part of the primary constructor. Very confusing. Yes, indeed.
In the example above, we also see the simplified syntax that Scala allows for a one-line method: def methodName() = body
. Scala will return the value of the last expression in the method, which in this case results in the value of the firstName
and lastName
fields being returned from the getFirstName()
and getLastName()
methods respectively.
Auxiliary constructors can be added to a class by adding a def this(params...)
definition for each such constructor that it is desired to add. We can for example add an initial letter and an auxiliary constructor in which it is set to the Person
class above.
package dtos
class PersonWithInitial(firstName : String, lastName : String) {
val fullName = firstName + " " + lastName
var initialLetter = ""
println("Inside the primary constructor.")
def this(firstName : String, lastName : String, initialLetter : String) {
this(firstName, lastName)
this.initialLetter = initialLetter
}
def getFirstName() = firstName
def getLastName() = lastName
def getInitialLetter() = initialLetter
def printName() {
println(fullName)
}
}
Listing 28: Person class where the person also can have an initial letter.
Note that the primary constructor is invoked from the auxiliary constructor using this(args...)
.
The new class can for example be used like this:
package testPackage
import dtos.PersonWithInitial
object PersonWithInitialTest {
def main(args : Array[String]) {
val adam = new PersonWithInitial("Adam", "Smith", "B")
println("The first name is: " + adam.getFirstName())
println("The last name is: " + adam.getLastName())
println("The initial letter is: " + adam.getInitialLetter())
print("The full name is: ")
adam.printName()
}
}
...
Inside the primary constructor.
The first name is: Adam
The last name is: Smith
The initial letter is: B
The full name is: Adam Smith
Listing 29: Program using the auxiliary constructor in PersonWithInitial.
Some other things about classes:
- The default access modifier is
public
. - Constructor params is by default
val
Static stuff - companion objects
What about static stuff? How do you add such things to a class? It turns out that you don't. Instead you create a companion object to the class. In that you put your static fields and methods. So how is such a companion object created? Like a regular class, except that the keyword object
is used instead of class
. For example, to create a companion object to the Person
class above, the following code could be used:
object Person {
var counter = 0
def incCounter() = counter += 1
def getCounter() = counter
}
Listing 30: Companion object to the Person class.
The companion object of a class must be located in the same file as the class itself. In our example the file contents of the Person.scala file looks like this:
package dtos
class Person(firstName : String, lastName : String) {
// The following does not work as constructor params by default is val.
//firstName = "Hej"
val fullName = firstName + " " + lastName
println("Inside the primary constructor.")
def getFirstName() = firstName
def getLastName() = lastName
def printName() {
println(fullName)
}
}
object Person {
var counter = 0
def incCounter() = counter += 1
def getCounter() = counter
}
Listing 31: Contents of the Person.scala file.
A small program that uses the Person
companion object can look like this:
package testPackage
import dtos.Person
object PersonWithStaticStuff {
def main(args : Array[String]) {
dtos.Person.incCounter()
println("Value of the counter in person is: " + dtos.Person.getCounter())
val adam = new dtos.Person("Adam", "Smith")
// This does not work:
//adam.incCounter()
}
}
Listing 32: Test program of the companion object to the Person class.
As the comments in the example program shows, it is not allowed to access static content through an instance variable, something that is legal, but frowned upon, in Java.
It is of course possible to define a main method inside a companion object, something that will be used in an example in the next section.
Inheritance
A class in Scala can inherit from exactly one parent class. The extends
keyword is used to specify which class to inherit from.
When defining a class inheriting from another class, the constructor of the class that is inherited from must be called by the primary constructor of the inheriting class. The override
keyword must be used when defining constructor parameters of the same name as those in the parent class.
The override
keyword is also used when overriding methods from the parent class. An example:
package dtos
class Programmer(override val firstName : String, override val lastName : String, val favouriteProgrammingLanguage : String) extends Person(firstName, lastName) {
val this.favouriteProgrammingLanguage = favouriteProgrammingLanguage;
def getFavouriteProgrammingLanguage() = favouriteProgrammingLanguage
override def printName() {
println(fullName + " : " + favouriteProgrammingLanguage)
}
}
object Programmer {
def main(args : Array[String]) {
val lars = new Programmer("Lars", "Nohle", "Python")
lars.printName()
}
}
...
Inside the primary constructor.
Lars Nohle : Python
Listing 33: Example of class inheritance.
In the example above we define a new class Programmer
that inherits from class Person
that we defined in an earlier section. We've added another constructor parameter to the existing two from Person
. The constructor params that we must pass to the super-constructor in Person
need to be tagged with override
. We've also overridden the printName()
method, which means that that method also needs to marked with the override
keyword.
Note that we've added a companion object defining the main
method to the file containing the Programmer
class. In main
, we've added a small code snippet exercising the Programmer
class.
Features
Traits
A trait is a collection of methods (contained in a file) that can be added to a class. A trait is similar to a mixin but with the difference that a trait cannot contain state. The reason for adding a trait to a class is to add new behaviour to the class without adding an is-a
relationship.
So how is a trait defined in Scala? It is (surprise!) defined using the trait
keyword. A class that want to use a trait does that by using the with
keyword if the class extends another class and using the extends
keyword if the class does not extend another class.
As an example, let's define a trait called CheckpointLoggable
that contains methods that can be used to send log messages to a centralized logging application.
package testPackage
trait CheckpointLoggable {
def log(msg : String) {
println("Sending the following message to the central logger: " + msg)
// Send message to the central logger.
//...
}
}
Listing 34: A trait that provides access to simple centralized, logging functionality.
The CheckpointLoggable
trait can now be used in a class like this:
package testPackage
class ImportantService extends Service with CheckpointLoggable {
// Many important methods...
}
object ImportantService {
def main(args : Array[String]) {
val service = new ImportantService()
service.log("testing...")
}
}
...
Sending the following message to the central logger: testing...
Listing 35: Class (inheriting from another class) that uses the CheckpointLoggable trait.
As in previous examples, the main method exercising the class in included in the companion object of the class.
If the class wanting to mix-in the trait wouldn't have inherited from another class, the syntax would have been like this:
package testPackage
class ImportantService extends CheckpointLoggable {
// Many important methods...
}
Listing 36: Class not inheriting from another class that uses the CheckpointLoggable trait.
XML
The XML support in Scala is very, very good. You can enter XML code directly in Scala code and assign it to a variable. That variable can then be used to process the XML data in various ways.
val books =
<books>
<book genre="Computer languages">
<title>Seven Languages in Seven Weeks</title>
<author>Bruce A Tate</author>
</book>
<book genre="Best practises">
<title>Effective Java</title>
<author>Joshua Bloch</author>
</book>
</books>
Listing 37: Assigning XML code to a variable.
All free text contained in the XML snippet can be extracted using the text
method.
println(books.text)
...
Seven Languages in Seven Weeks
Bruce A Tate
Effective Java
Joshua Bloch
Listing 38: Extracting free text from an XML block..
Scala also supoorts an XPath-like syntax for extracting nodes and attribute values from an XML block. To extract from the top node only \
is used:
val bookSequence = books \ "book"
Listing 39: Extracting subelements of the top level element...
The returned nodes can then be access by position or in sequence using for example foreach
:
// Fetch the first book.
println("The first books is: " + bookSequence(0))
// Loop through the books.
println("The books:")
bookSequence.foreach {book =>
println("Book: " + book.text)
}
...
The first books is:
Seven Languages in Seven Weeks
Bruce A Tate
The books:
Book:
Seven Languages in Seven Weeks
Bruce A Tate
Book:
Effective Java
Joshua Bloch
Listing 40: Accessing the extracted elements.
To extract nodes from the whole XML document, not only from the top node, \
is used:
// Extract and print the titles of the books.
val titlesSequence = books \ "title"
println("The titles of the books are: ")
titlesSequence.foreach {title => println(title.text)}
...
The titles of the books are:
Seven Languages in Seven Weeks
Effective Java
Listing 41: Extracting elements from the whole document.
To retrieve attribute values, @
is used in the query:
// Extract and print the genres.
println("The genre attributes of the book elementa are; ")
val genresSequence = books \ "@genre"
genresSequence.foreach {genre => println(genre)}
...
The genre attributes of the book elementa are;
Computer languages
Best practises
Listing 42: Extracting attribute values.
Regexps
Scala strings have a nice method called r()
that translates a string to a regular expression of type scala.util.matching.Regex
.
The scala.util.matching.Regex
contains a lot of useful methods. Using that it is possible to performing the usual regexp stuff that we all love to perform, e.g. searching, replacing and grouping. Searching can be performed by one of the various findXXX()
methods (findFirstIn()
, findAllIn()
, ...), replacing with replaceXXX()
(replaceFirstIn()
, replaceAllIn()
, ...).
The following program is an example of using regexps in Scala:
import scala.util.matching.Regex
def testIfMatches(pattern : Regex, stringToExamine : String) {
if (pattern.findFirstIn(stringToExamine) != None) {
println("'" + stringToExamine + "'" + " matches")
} else {
println("'" + stringToExamine + "'" + " don't match")
}
}
val pattern = "^(h|H)ello".r
testIfMatches(pattern, "Hello")
testIfMatches(pattern, "hello")
testIfMatches(pattern, "A Hello")
...
'Hello' matches
'hello' matches
'A Hello' don't match
Listing 43: Using a regexp to perform matching.
Package objects
Package objects something that I've not encountered in another language. In Scala, each package can have zero or one package objects in which various definitions can be placed. These definitions will then automatically be part of every member of the package. It is also possible to import these definitions into other Scala programs using the syntax import packageWithPackageObject._
.
A package object is defined in a file named package.scala
. That file should have a package
that includes all parent packages of the package in question, but not the package itself. The name of the package is instead specified in the package object
statement in which the content that should be shared with all members of the package is defined.
As a simple example, let's create a package object for the package apkg
. The package object should only contain a single integer constant. As the apkg
package does not have any parent packages, the package object should not contain any package
statement.
// In file apkg/package.scala
package object apkg {
val A_CONSTANT = 42
}
Listing 44: Simple package object.
To demonstrate the that all members of the package apkg
get access to the A_CONSTANT
we create a small test program that just prints the value of that constant.
package apkg
object ATestProgram {
def main (args : Array[String]) {
println("The value of the constant is: " + A_CONSTANT)
}
}
...
The value of the constant is: 42
Listing 45: Using the declarations in a package object from a object in the same package..
Gotchas
- A public class in Scala can have a different name compared to the file it is located in.
- The names of many common datatypes is different from those in Java: Int, Boolean, ...
FizzBuzz in Scala
for (i <- 1 until 101) {
if (i % 3 == 0 && i % 5 == 0) println("FizzBuzz") else if (i % 3 == 0) println("Fizz") else if (i % 5 == 0) println("Buzz") else println(i)
}
Listing 46: FizzBuzz in Scala. Using a too long line...;)
What I like about Scala
- The syntax for creating one line methods:
def f() = body
- The syntax for getting and setting values in maps:
aMap(key) = value;val v = aMap(key)
- The XML support. Is it astonishing!
What I don't like about Scala
- Compilation and interpretation of Scala programs are slow. Really slow. I can't remember any other language for which compilation and interpretation are so slow.
- The keyword that should be used to mix in a trait is different when the class wanting to use the trait inherits from another class (
with
) and when it does not inherit from another class (extends
). Why do this? It's just adds unnecessary confusion.
Onwards, upwards
That was Scala. Many nice features it has, but now it's time for something entirely different: Erlang! Auf wiedersehen!
Previous parts in the series
1. Ruby2. Io
3. Prolog
Links
[1] Scala Development Kit Download[2] Scala Homepage
[3] Scala Lists
[4] Scala Sets
[5] Scala Maps