Getting Started with Scala
Download
Report
Transcript Getting Started with Scala
Getting Started with Scala
26-Mar-16
Getting Scala
Download ScalaIDE: http://scala-ide.org/
This is a customized version of Eclipse
Already using Eclipse? It’s okay to have more than one version
Scala can also be obtained by itself:
http://www.scala-lang.org/downloads
You can use the Scala documentation online,
http://www.scala-lang.org/downloads
or download it and use it locally,
http://www.scala-lang.org/downloads
Documentation is not always complete or easy to read
The source is directly available from the documentation
Hello World
/** Everybody's first program */
object HelloWorld {
def main(args: Array[String]) {
println("Hello, World!")
}
}
Save on a file named HelloWorld.scala
Compile with scalac HelloWorld.scala (or from your IDE)
Run with scala HelloWorld (or from your IDE)
Generate documentation with scaladoc HelloWorld.scala
The above code creates a singleton object
Everything in the object is like Java’s static
Scala doesn’t have static
Scala does have classes; we’ll get to those later
3
Comments
// and /*...*/ comments are as in Java
/** Scaladoc comments are similar to Javadoc
* comments. As in Javadoc, the first sentence
* is a summary sentence, and additional
* sentences give more detail. However, the
* formatting convention is slightly different,
* and wiki markup may be used instead of
* HTML markup. Most of the same flags
* (@author, etc.) are used. */
def doNothing = ()
Types
The type hierarchy is a lattice, not a tree
That is, it has a “bottom” as well as a “top”
Any
AnyVal
AnyRef (corresponds to Object in Java)
Boolean, Char, Byte, Short, Int, Long, Float, Double
Scala has no primitives—these are objects
Unit (has only a single value, ()
Unit is returned by functions that “don’t return anything” (e.g.
println)
All Java reference types, for example, String
ScalaObject
All Scala reference types, including Array and List
Null (bottom of all AnyRef objects)
Nothing (bottom of Any)
5
Declaring variables and values
The syntax is
var name: type = value // declares a variable
val name: type = value // declares a value
Values are immutable: they can’t be changed
The : type can almost always be left off—the type is inferred from the value
The = value must (almost) always be supplied
This also works: var x: Double = 5
Style rule: use val instead of var wherever you can
Rules for alphanumeric names are just like in Java
But there are other kinds of names
Scope rules are almost the same as those in Java
Capitalization conventions are just like in Java
Arithmetic, comparison, and logical operators are just like in Java
Indentation is 2 spaces, not 4, but is otherwise like Java
6
“Statements”
Scala’s “statements” should really be called “expressions,” because every
statement has a value
The value of many statements, for example the while loop, is ()
The value of a if or match statement is the last value computed
The value of a block, {…}, is the last value computed in the block
A statement is ended by the end of the line (not with a semicolon) unless it is
obviously incomplete, or if the next line cannot begin a valid statement
() is a value of type Unit
() is the only value of type Unit
() basically means “Nothing to see here. Move along.”
For example, x = 3 * (2 * y + is obviously incomplete
Because Scala lets you leave out a lot of unnecessary punctuation, sometimes a line
that you think is complete really isn’t complete (or vice versa)
You can end statements with semicolons, but that’s not good Scala practice
7
Familiar statement types
These are the same as in Java, but have a value of ( ):
variable = expression // also +=, *=, etc.
while (condition) { statements }
do { statements } while (condition)
These are the same as in Java, but may have a useful value:
{ statements }
if (condition) { statements } else { statements }
The value is the value of whichever block is chosen
If the value is to be used, both blocks should have the same type, otherwise
the type of the result is the “least upper bound” of the two types
if (condition) { statements }
The value of the block is the last value computed in it
The value is the value of the last statement executed, but its type is Any –
if you want a value, you really should use an else
As in Java, braces around a single statement may be omitted
8
The for comprehension
Scala’s for is much more powerful than Java’s for
We will just cover some simple cases here
for (i <- 1 to 10) { println(i) }
Prints all the values in myArray
for (x <- myArray) { println(x) }
Prints the numbers 1 through 9
for (x <- 0 until myArray.length) { println(myArray(x)) }
Prints the numbers 1 through 10
for (i <- 1 until 10) { println(i) }
Consequently, it is used much more often than the other kinds of loops
Prints all the values in myArray
for (x <- myArray
if x % 2 == 0) { println(x) }
Prints all the even numbers in myArray
Arrays
Arrays in Scala are parameterized types
When no initial values are given, new is required, along with an
explicit type:
Array[String] is an Array of Strings, where String is a type
parameter
In Java we would call this a “generic type”
val ary = new Array[Int](5)
When initial values are given, new is not allowed:
val ary2 = Array(3, 1, 4, 1, 6)
Array syntax in Scala is just object syntax
Scala’s Lists are more useful, and used more often, than Arrays
val list1 = List(3, 1, 4, 1, 6)
val list2 = List[Int]() // An empty list must have an
explicit type
Multidimensional arrays
To create a 2-dimensional array or higher, you need to use the
toDim method
scala> val twoDim = Array.ofDim[Int](2, 3)
twoDim: Array[Array[Int]] = Array(Array(0, 0, 0),
Array(0, 0, 0))
Once created, it’s used very much like an array in Java (but with
parentheses rather than brackets)
scala> twoDim(1)(2) = 99
scala> twoDim
res2: Array[Array[Int]] = Array(Array(0, 0, 0),
Array(0, 0, 99))
Simple List operations
By default, Lists, like Strings, are immutable
Basic operations:
list.head (or list head) returns the first element in the list
list.tail (or list tail) returns a list with the first element removed
list(i) returns the ith element (starting from 0) of the list
list(i) = value is illegal (immutable, remember?)
value :: list returns a list with value appended to the front
list1 ::: list2 appends (“concatenates”) the two lists
list.contains(value) (or list contains value) tests whether value is in list
Many operations on Lists also work on other kinds of Sequences
An operation on a Sequence may return a Sequence of a different type
Operations on an immutable List return a new List
scala> "abc" :: List(1, 2, 3)
res22: List[Any] = List(abc, 1, 2, 3)
This happens because Any is the only type that can hold both integers and strings
There are over 150 built-in operations on Lists—use the API!
for…yield
for returns Unit, but for…yield returns a sequence of
values
Where possible, it returns the same type of sequence as it
operates on
scala> for (i <- List(1, 2, 3)) yield { 2 * i }
res1: List[Int] = List(2, 4, 6)
scala> for (i <- 97 to 100) yield { i.toChar }
res2: scala.collection.immutable.IndexedSeq[Char] =
Vector(a, b, c, d)
scala> for (ch <- "abcd" if ch != 'c') yield { ch.toInt }
res3: scala.collection.immutable.IndexedSeq[Int] =
Vector(97, 98, 100)
Tuples
Scala has tuples, up to size 22 (why 22? I have no idea.)
Tuples are referenced starting from 1, using _1, _2, ...
scala> val t = Tuple3(3, "abc", 5.5)
t: (Int, java.lang.String, Double) = (3,abc,5.5)
scala> val tt = (3, "abc", 5.5)
tt: (Int, java.lang.String, Double) = (3,abc,5.5)
scala> val t = ('a', "one", 1)
t: (Char, String, Int) = (a,one,1)
scala> t._2
res3: String = one
Tuples, like Lists, are immutable
Tuples are a great way to return more than one value from a
method
An aside...abbreviations
Scala lets you omit a lot of “unnecessary” punctuation
For example,
if (name startsWith "Dr.") { ... }
is more readable (and easier to type) than
if (name.startsWith("Dr.")) { ... }
Readability matters!
Therefore, you should experiment with leaving out
punctuation anywhere you think it might be okay
However,
If you get mysterious syntax errors, try putting the punctuation
back in, both on this line and on the previous line
Maps
scala> val m = Map("apple" -> "red", "banana" ->
"yellow")
m:
scala.collection.immutable.Map[java.lang.String,java.lang
.String] = Map((apple,red), (banana,yellow))
Notice that a Map is really just a list of Tuples
The -> is provided as a more readable syntax
scala> m("banana")
res2: java.lang.String = yellow
scala> m contains "apple"
res3: Boolean = true
scala> m("cherry")
java.util.NoSuchElementException: key not found: cherry
Simple method definitions
def isEven(n: Int) = {
val m = n % 2
m == 0
}
def isEven(n: Int) = n % 2 == 0
The result is the last value (in this case, a Boolean)
This is really kind of poor style; the extra variable isn’t needed
This is much better style
The result is just a single expression, so no braces are needed
def countTo(n: Int) {
for (i <- 1 to 10) { println(i) }
}
It’s good style to omit the = when the result is ()
If you omit the =, the result will be ()
You need either braces or an =; you can’t leave out both
More method definitions
def half(n: Int): Double = n / 2
You can state the result type explicitly
In this example, half(7) will return 3.5 (!)
def half(n: Int): Int = return n / 2
If you use a return statement, you must state the result type explicitly
Good style in Scala is to avoid using return statements, and just putting the
return value as the last thing in the method
Methods and functions
A method is a function that belongs to an object
A function does not belong to any object
Methods usually use, and manipulate, the fields of an object
The inputs to a function are, or should be, just its parameters
The result of calling a function is, or should be, just its return
value
Scala can sometimes accept a method where a function
is expected
A method can sometimes be “converted” to a function by
following with an underscore (for example, isEven _)
Functions are first-class objects
Functions are values (like integers, etc.) and can be assigned to
variables, passed to and returned from functions, and so on
Wherever you see the => symbol, it’s a literal function
Example (assigning a literal function to the variable foo):
scala> val foo =
(x: Int) => if (x % 2 == 0) x / 2 else 3 * x + 1
foo: (Int) => Int = <function1>
scala> foo(7)
res28: Int = 22
The basic syntax of a function literal is
parameter_list => function_body
In this example, foreach is a function that takes a function as a
parameter:
myList.foreach(i => println(2 * i))
Functions as parameters
To define a function, you must specify the types of each of its parameters
Therefore, to have a function parameter, you must know how to write its type:
(type1, type2, ..., typeN) => return_type
type => return_type // if only one parameter
() => return_type // if no parameters
Example:
scala> def doTwice(f: Int => Int, n: Int) = f(f(n))
doTwice: (f: (Int) => Int,n: Int)Int
scala> def collatz(n: Int) = if (n % 2 == 0) n / 2 else 3 *
n + 1
collatz: (n: Int)Int
scala> doTwice(collatz, 7)
res2: Int = 11
scala> doTwice(a => 101 * a, 3)
res4: Int = 30603
Higher-order methods on Lists
map applies a one-parameter function to every element of a List, returning a
new List
scala> def double(n: Int) = 2 * n
double: (n: Int)Int
scala> val ll = List(2, 3, 5, 7, 11)
ll: List[Int] = List(2, 3, 5, 7, 11)
scala> ll map double
res5: List[Int] = List(4, 6, 10, 14, 22)
scala> ll map (n => 3 * n)
res6: List[Int] = List(6, 9, 15, 21, 33)
scala> ll map (n => n > 5)
res8: List[Boolean] = List(false, false, false, true, true)
filter applies a one-parameter test to every element of a List, returning a
List of those elements that pass the test
scala>
res10:
scala>
once
res11:
ll filter(n => n < 5)
List[Int] = List(2, 3)
ll filter (_ < 5) // abbreviated function where parameter is used
List[Int] = List(2, 3)
More higher-order methods
def filterNot(p: (A) => Boolean): List[A]
def count(p: (A) => Boolean): Int
Tests whether a predicate holds for at least one of the elements of this list
def find(p: (A) => Boolean): Option[A]
Tests whether a predicate holds for every element of this list
def exists(p: (A) => Boolean): Boolean
Counts the number of elements in the list which satisfy a predicate
def forall(p: (A) => Boolean): Boolean
Selects all elements of this list which do not satisfy a predicate
Finds the first element of the list satisfying a predicate, if any
def sortWith(lt: (A, A) => Boolean): List[A]
Sorts this list according to a comparison function
Explicit pattern matching
Explicit pattern matching is done with the match
method:
expression match {
case pattern1 => expressions
…
case patternN => expressions
}
Pattern matching
Pattern matching on literal values:
today match {
case "Saturday" => println("Party! Party! Party!")
case "Sunday" => println("Pray....")
case day => println(day + " is a workday. :( ")
}
Pattern matching on types:
something match {
case x: Int => println("I'm the integer " + x)
case x: String =>
println("I'm the String \"" + x + "\"")
println("My length is " + x.length)
case _ => println("I don't know what I am! :( ")
}
The Option type
Scala has null because it interoperates with Java; it shouldn’t be
used any other time
Instead, use an Option type, with values Some(value) and None
def max(list: List[Int]) = {
if (list.length > 0) {
val big = list reduce {(a, b) => if (a > b) a else b}
Some(big)
} else {
None
}
max(myList) match {
case Some(x) => println("The largest number is " + x)
case None => println("There are no numbers here!!!")
}
The require and assert methods
require and assert are methods that throw an exception when
their argument is false
require is used to document something that must be true in
order for the code to work
def sqrt(x: Double) = { require(x >= 0); ... }
require is often used at the beginning of a method
assert is used to document something that you “know” to be
true
takeCis554course
assert(languagesIKnow contains "Scala")
assert is often used as “executable documentation”
The ensuring method
The ensuring method applies a predicate to a value and, if the predicate
result is true, returns the value, otherwise throws an AssertionError
Syntax: value ensuring(predicate)
scala> 12 ensuring(true)
res1: Int = 12
scala> 12 ensuring(_ > 10)
res2: Int = 12
scala> def twice(x: Int) = 2 * x ensuring(_ > 0)
twice: (x: Int)Int
scala> twice(3)
res3: Int = 6
scala> twice(-5)
java.lang.AssertionError: assertion failed (+ many lines)
ensuring can be useful to guarantee the result of a method
Always use _ in the predicate to represent the method’s return value
Dealing with exceptions
Scala’s exception creation and throwing is like Java:
class RottenEggException extends Exception
throw new RottenEggException
Catching a thrown exception uses pattern matching:
try {
makeAnOmlet
} catch {
case ex: RottenEggException => println("#$%&#@")
case ex: Exception => println("What went wrong?")
}
Console I/O
val data = readLine // Reads a single line from the
Console
print(data)
newline
// Prints to the Console without a
println(data)
newline
// Prints to the Console with a
File I/O
object TestIO {
def main(args: Array[String]) {
println("Testing file I/O")
import java.io._
import scala.io.Source
val file = new File("testio.txt")
val writer = new PrintWriter(file)
writer write "first\r\n"
writer write "second"
writer.close()
Use correct case for file names
(only Windows ignores case)
Use forward slashes, /, in paths,
which work on any platform, not
backslashes, \, which work only
on Windows
Windows, Mac, and Linux have
different end-of-line codes (\r\n
is Windows), and this causes
problems
val lines =
Source.fromFile(file).getLines().toList
println(lines)
}
}
Testing file I/O
List(first, second)
Use the source, Luke
Books and tutorials are good for learning the syntax of Scala, but they
aren’t much help learning the API
Unfortunately, Scala’s API documentation isn’t very complete
The good news is, it’s easy to get to the source code—and in most cases,
the source code is easier to read than you might think
The End
33