260 likes | 428 Views
Getting Started with Scala. 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)
E N D
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 single 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
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 is used in preference to * 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 • 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) • AnyRef (corresponds to Object in Java) • 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)
Declaring variables and values • The syntax isvar name: type = value// declares a variableval 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 = valuemust (almost) always be supplied • This also works: var x: Double = 5 • 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 the same as in Java
“Statements” • Scala’s “statements” should really be called “expressions,” because almostevery statement has a value • For example, the value of a = 5 is 5 • The value of many statements, for example the while loop,is () • () is a value of type Unit • () is the only value of type Unit • () basically means “Nothing to see here. Move along.” • 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 • 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
Familiar statement types • These are the same as in Java (but have a value): • variable = expression// also +=, *=, etc. • The value of the statement is the value of the variable • while (condition) { statements } • The value of the statement is () • do { statements } while (condition) • The value is () • if (condition) { statements } • The value is the value of the last statement executed • if (condition) { statements } else { statements } • Older descriptions of Scala say that the else is required. It isn’t--if omitted and the condition is false, the value of the if statement is ()
The for comprehension • Scala’s for is much more powerful than Java’s for • Consequently, it is used much more often than the other kinds of loops • We will just cover some simple cases here • for (i <- 1 to 10) { println(i) } • Prints the numbers 1 through 10 • for (i <- 1 until 10) { println(i) } • Prints the numbers 1 through 9 • for (x <- 0 until myArray.length) { println(myArray(x)) } • Prints all the values in myArray • for (x <- myArray) { println(x) } • 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 • Array[String] is an Array of Strings, where String is a type parameter • In Java we would call this a “generic type” • When no initial values are given, new is required, along with an explicit type: • val ary = new Array[Int](5) • When initial values are given, new is not allowed: • val ary2 = Array(3, 1, 4, 1, 6) • Arrays 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
Simple List operations • By default, Lists, like Strings, are immutable • Operations on an immutable List return a new List • 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 • An operation on a List may return a List of a different type • scala>"abc" :: List(1, 2, 3)res22: List[Any] = List(abc, 1, 2, 3) • There are over 150 built-in operations on Lists--use the API!
Tuples • Scala has tuples, up to size 22 (why 22? I have no idea.) • 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) • Tuples are referenced starting from 1, using _1, _2, ... • scala> t._1res28: Int = 3 • t _1also works (the dot is optional) • 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) thanif (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 function definitions • def isEven(n: Int) = { val m = n % 2 m == 0} • The result is the last value (in this case, a Boolean) • def isEven(n: Int) = n % 2 == 0 • 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 () • 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
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 + 1foo: (Int) => Int = <function1>scala> foo(7)res28: Int = 22 • The basic syntax of a function literal isparameter_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)Intscala> def collatz(n: Int) = if (n % 2 == 0) n / 2 else 3 * n + 1collatz: (n: Int)Intscala> doTwice(collatz, 7)res2: Int = 11scala> 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 * ndouble: (n: Int)Int • scala> val ll = List(2, 3, 5, 7, 11)ll: List[Int] = List(2, 3, 5, 7, 11) • scala> ll map doubleres5: 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> ll filter(n => n < 5)res10: List[Int] = List(2, 3) • scala> ll filter (_ < 5) // abbreviated function where parameter is used onceres11: List[Int] = List(2, 3)
More higher-order methods • def filterNot(p: (A) => Boolean): List[A] • Selects all elements of this list which do not satisfy a predicate • def count(p: (A) => Boolean): Int • Counts the number of elements in the list which satisfy a predicate • def forall(p: (A) => Boolean): Boolean • Tests whether a predicate holds for every element of this list • def exists(p: (A) => Boolean): Boolean • Tests whether a predicate holds for at least one of the elements of this list • def find(p: (A) => Boolean): Option[A] • 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
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 biggest = (list(0) /: list) { (a, b) => if (a > b) a else b }Some(biggest) } 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 • takeCis700courseassert(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
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?")}
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() val lines = Source.fromFile(file).getLines().toList println(lines) }} • Testing file I/OList(first, second) • 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
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