0

back to all projects

LEARNING SCALA

BASICS OF SCALA, OBJECT-ORIENTED SCALA, FUNCTIONAL PROGRAMMING SCALA


SCALA

HARD

last hacked on Aug 13, 2017


Scala is a general-purpose programming language that features both object-oriented and functional programming attributes. Scala was first released in 2004 by Martin Odersky, striving to add the functional paradigm to the already object-oriented Java platform. As such, Scala is highly scalable, extensible, and portable. This makes the Scala language a very attractive programming language in the world of big data processing. **Scala is object-oriented.** Scala is said to be a *pure* object-oriented language in that every value is an object; hence, Scala does not have _primitive_ data types. And, since all data types are objects, they all have defined properties and callable methods. **Scala is functional.** Scala is functional in that every function is a value. And since every value is an object, every function is an object. Scala features support for anonymous functions, higher-order functions, nesting, and currying. **Scala is compiled.** Scala source code (just as Java source code) compiles to Java bytecode, and then it runs on the Java Virtual Machine (JVM). The JVM is an abstract computing machine that enables a computer to run a Java program.

# Scala Basics ## Setup Scala 1. Download an IDE like Intellij or Eclipse. 2. Set up the IDE with Java 3. Find and install the Scala plugin within the IDE 4. Create a new Scala project 5. Create a worksheet within the _src_ folder. You can type Scala code here and immediately see the output. 6. Create a Scala class within the _src_ folder. ## Data Types `Boolean` * True or false `Byte` * 8-bit signed integer `Char` * 16-bit signed integer `Short` * 16-bit signed integer `Int` * 32-bit signed integer `Long` * 64-bit signed integer `Float` * 32-bit signed integer `Double` * 64-bit signed integer ## Data Structures Scala data structures are known as _collections_. Data structures, as the name suggests, are structures of data; which, in the case of Scala, represent structures of objects. ### Lists Scala lists are ordered, linear sets of items. Each element within a list contains an object, which may be any data type. Here, we declare a list of integers: scala> val intsList = List(1, 2, 3, 4, 5) And here, we declare a list of mixed data types: scala> val mixedList = List(1, "David", List(7,3), 4, 5) Notice that `mixedList` contains integers, a string, and a list of its own. ### Sets Scala sets are unordered sets of unique elements. Because they are unordered, you may not access its elements by index. Here, we declare a set of integers: scala> val intsSet = Set(1, 2, 3, 4, 5) Sets provide useful functions such as `min`, `max`, `count`, `find`, `filter`, `diff`, `intersect`, and `union`. Let's apply the `min` function to `intsSet`: scala> intsSet.min ### Tuples Tuples can store many different data types. You access an element in a Tuple by code like `._1`. This can be useful in returning many values from a function or map. val tuple = (23, "Foo") println(tuple.\_1) println(tuple.\_2) This tuple is of type `Tuple[Integer, String]`. You can also have tuples like `Tuple[Integer, String, String, String, Double]`. ### Maps Pass a key into the map and it returns the value. Maps are immutable by default, but there are imports available to make them mutable. Changing an immutable map will create a new object. Add new elements to a set with the `+` operator; you can also use `+=` if your variable is a var or the collection is mutable. val map = mutable.Map[Int, String]() map += (1 -> "Step one") map += (2 -> "Step two") map += (3 -> "Step three") println(map(2)) // This will print "Step two" Here the numbers are keys and the strings are values. ## Hello World Open terminal and `cd` to the directory of choosing. Then create the source code file: $ touch HelloWorld.scala Open `HelloWorld.scala` with your preferred text editor or IDE and input this source code: object HelloWorld { def main(args: Array[String]) { println("Hello World!") } } Compile the source code. Enter into the terminal: $ scalac HelloWorld.scala This should create two files: `HelloWorld.class` and `HelloWorld$.class`. These files contain bytecode, which runs on top of the Java Virtual Machine (JVM). To run your program, enter into your terminal: $ scala HelloWorld ## If Then Create the source file: $ touch IfThen.scala Open `IfThen.scala` and input this source code: object IfThen { def main(args: Array[String]) { if(2==2){ println("2 equals 2!"); } } } Compile the source code. Enter into the terminal: $ scalac IfThen.scala To run your program, enter into your terminal: $ scala IfThen ## While Loop Create the source code file: $ touch WhileLoop.scala Open `WhileLoop.scala` and input this source code: object WhileLoop { def main(args: Array[String]) { var x = 0; while (x <= 10){ println(x); x = x + 1; } } } Compile the source code; enter into the terminal: $ scalac WhileLoop.scala Run your program; enter into the terminal: $ scala WhileLoop ## For Loop Create the source code file: $ touch ForLoop.scala Open `ForLoop.scala` and input this source code: object ForLoop { def main(args: Array[String]) { for (x <- 0 to 10) { println(x); } } } Compile the source code: $ scalac ForLoop.scala Run the bytecode: $ scala ForLoop ## Filtering Use filtering like this for ( i <- 0 to 10 if i != 6 if i % 2 == 0 ) println(i) ## Match Expressions Use match by doing this. val output = // optional to assign to a variable myVariableName match { case "abc" => "1" case "abcd" => "2" case "abcde" => "3" case _ => "?" } Match is powerful in Scala, you can search for different classes or data types at each case statement. ## Break / Continue Break and continue aren't very functional, so it is recommended to use recursion instead. ## Read from a File Reading from files is common for scripts. import scala.io.Source if (args.length > 0) { for (line <- Source.fromFile(args(0)).getLines()) println(line.length + " " + line) } else Console.err.println("Please enter filename") If you want to reuse lines, you can save them to a list. val lines = Source.fromFile(args(0)).getLines().toList for (line <- lines) { ... } The function `reduceLeft` will take the first two arguments, reduce them, then reduce the answer with the next argument. val longestLine = lines.reduceLeft( (a, b) => if (a.length > b.length) a else b ) # Functions In Scala, functions are named, reusable expressions. They may be parameterized, and may return a value; neither are required. Scala features _pure_ functions. In Scala, a _pure_ function is one that: * Has one or more input parameters * Performs calculations using only the input parameters * Returns a value * Always returns the same value for the same input * Does not use or affect any data outside the function * Is not affected by any data outside of the function Pure functions are more stable than non-pure functions because they are stateless and orthogonal to external data. The general syntax for a function is as follows: def <identifier>(<identifier>: <type>[, ...]): <type> = <expression> Let's define an `adder` function: def adder(a: Int, b: Int): Int = { a + b } Let's define an `power` function: def power(x: Int, n: Int): Int = { if(n >= 1) x * power(x, n - 1) else 1 } # Object-Oriented Programming in Scala ## Objects Object-oriented programming has been adopted by almost every modern language and is needed to add structure to a program. An object is a container that combines data and operations and can implement an interface. Objects are service providers. If I could magically pull an object out of a hat, which objects would solve my problem? Objects make programs less complicated and are intuitive because they closely reflect the way humans think. An object is made up of fields, getters/setters, functions, and a constructor. When you compare two objects use the equals function instead of `==`. You can save objects after a program is finished by saving it to a database. * **fields**: variables that the object stores * **getters/setters**: setters let you set new values to the fields and getters let you get the values in the fields * **functions**: behavior of the object * **constructor**: code you want to run when the object is created ## Classes The blueprint for creating an object is called a class. There are two types of programmers, class creators and client programmers. Class creators expose what is necessary with interfaces but keeps the rest hidden. The client programmer collects these classes and uses them to solve problems. You create a class to define your own blueprint for an object. ### Preconditions To make sure data is valid at construction, you can add a precondition that will return `IllegalArgumentException` if a test fails. class Balance(n: Int) { require (n >= 0) ... } ### Constructors Scala saves space and lets you define the parameters for your _primary constructor_ on the same line as the `class` keyword. class Dog(name: String) { ... } If you would like to have multiple constructors, just define a function called `this` inside the class that calls the _primary constructor_. ## Inheritance Inheritance is useful, but even the creator of Java jokingly said inheritance was their biggest mistake. Inheritance is similar to cloning a class and providing slightly different behavior and maybe different fields. Inheritance is done by using the "extends" keyword in the class definition. Try to rarely use inheritance unless it is a clear choice. The two main reasons to use inheritance are: * Add a new function (consider if the main class could just have this added) * Change an existing function (overriding) Inheritance should have an _is-a_ relationship such as "a dog _is-a_ animal" or "a circle _is-a_ shape". So to reiterate, if you need a dog and already have an animal class, you might choose to create a dog class that inherits from animal. If you have _is-like-a_ relationship you might turn to inheritance too. In this case, if you need two classes that are similar, you might create a third abstract class to be the parent of both classes. Creating this third class can help the design better follow these principles. To use inheritance in Scala use the `extends` keyword. class MySubclass(param1: Int) extends MySuperclass { def ... } To invoke the superclass with a parameter, use code like this: class MySubclass(param1: Int) extends MySuperclass(param1) { def ... } ## Polymorphism The behavior of a function is determined at runtime instead of compile time. This can happen when the function is part of an interface or on a class that has subclasses. ## Encapsulation To use encapsulation set the fields to private, all tasks that the object should be doing should live inside that class. This means that if class B needs something to be done and it makes sense for class A to do it, class B may need to delegate work to class A. ## Final The `final` keyword is used to prevent * reassignment of fields (add `final` before `val`) * overriding of methods (add `final` before `def`) * a class from being subclassed (add `final` before `class`) ## Common Class Methods * **`==` and `equals`**: You override `equals(that: Any)` like in Java to test for equality of two objects. The `==` function simply calls `equals` but also accounts for null values beforehand. You only need to override `equals`. You must override hashCode if you override equals. * **`hashCode`**: Must be overriden if `equals` is overriden. The internal logic you use inside the function body is similar to Java. override def hashCode: Int = { // implementation here } * **`toString`**: Override this to give a readable define how your class should be represented when displayed as a String. # Functional Programming in Scala I like turtles and functions. ## First-Class Functions I like turtles. ## Anonymous Functions I like turtles. ## Higher-Order Functions I like turtles. ## Closures I like turtles. ## Currying I like turtles. ## Scala Class Hierarchy * **Any**: The superclass of everything * **AnyVal**: Types like Int, Double, Char, Boolean, and Unit * **AnyRef**: Types like Strings and collections * **Null and Nothing**: A subclass of every object. You may choose for a function to return Nothing if it throws exceptions. Also, you can have an if/else expression return an Int in one branch and Nothing in the other branch. ## Traits To create a trait, define it similar to a class, but use the keyword `trait` instead of `class`. You need to use the word `extends` or `with` when adding traits. Traits is when you have an _implements_ relationship, but in Scala, you can define the implementation inside the trait itself and store fields. In Scala, if you implement a trait and define one or two function implementations you can even gain access to dozens of methods for your class. ## Ordering There is a predefined trait called `Ordered[yourDataTypeHere]` that you can add to your class. You must define a function called `compare` and then you will automatically get functions like `<`, `>`, and sorting. def compare(that: Rational) = (this.numer * that.denom) - (that.numer * this.denom) } If `compare` returns positive, _this_ is greater. ## Packages Scala code is put in a package by placing `package your.package.name` at the top of the file. Packages should only include letters and periods. Import a package to use it in another file. You do with by placing `import your.package.name` near the top of the file. ## Access Modification * **private**: Use the `private` keyword before a field or function to make it accessible only to that object or class. * **protected**: The `protected` keyword is similar to `private`, but also allows access to the subclasses. * **accessible to all**: Use no keyword and the member will be visible to others. ## Assertions and Tests Use `assert(yourBooleanCondition)` and it will throw an assertion error if it is false. One way to make tests is to write code like this and extend FunSuite import org.scalatest.FunSuite import your.package.here class YourObjectSuite extends FunSuite { test("Describe what you are testing") { // assemble val x = ... // act ... // assert assert(yourBooleanCondition) } } In ScalaTest, when you run a suite, all nested suites are executed. You can run a test suite from the command line like this: scala -cp scalatest.jar YourSuiteFile scala -cp scalatest.jar org.scalatest.run YourSuiteFile ## Case Classes and Pattern Matching To make a class pattern matchable, usually all you need is to add the `case` keyword before each class definition. * First, the `case` keyword will make it so you don't need to provide the `new` keyword when creating a new object of the class. val myObject = MyClass("parameter") * Also the parameters are set as `val` fields. * Some functions are set up by default like `toString`, `hashCode`, and `equals`. * A function called `copy` is available that allows you to copy your object. Here are examples of pattern matching: def myFunc(x: Any) = x match { case 2 => "two" case _ => "not two" } y match { case aValue => "same as aValue" case _ => "another value" } z match { case List(_, _, _) => "three element list" case Dog => "a dog" case (a, b, c) => "matched a tuple of size three" case s: String => s case a :: b :: _ => b // returns the second element in a list } ## Option Type Scala's maps return `Some(value)` or `None` which are both of type `Option[String]`. So you can do this: val map = Map(1 -> "one", 2 -> "two") def myFunc(x: Option[String]) = x match { case Some(s) => s case None => "nothing" } myFunc(map get 2) // returns "two" myFunc(map get 3) // returns "nothing" ## More Patterns This is an example of a for loop that only iterates on the elements that match the pattern. val myList = List(Some(1), Some(2), None) for (Some(item) <- myList) println(item) // prints 1 and 2 but not None for ((key, value) <- myMap) println(s"The key is $key and the value is $value") ## More with Lists ### Main Uses * **`head`**: return the first element * **`tail`**: return the list of all elements but the first * **`isEmpty`**: returns a Boolean * **`:::`**: Used to join two lists You often use pattern matching with lists. val List(x, y, z) = myList // can define three variables: x, y, and z val x :: y :: others = myList // can define three variables: x, y, and others. others would be a List. ### Other Uses * **`last`**: return the last element (must traverse the whole list) * **`init`**: return the list of all elements but the last (must traverse the whole list) * **`reverse`**: reverses a list * **`length`**: get the length * **`drop`**: drop the first n elements * **`take`**: keep the first n elements * **`splitAt`**: split at an index * **`flatten`**: transforms a List of Lists into a List * **`zip`**: take two lists and combine them into a Tuple list; matching by elements * **`zipWithIndex`**: zips the list with the index * **`unzip`**: unzips; turning a List of Tuples into a Tuple of Lists * **`toString` and `mkString`**: turns a list into a string Pattern matching are useful with Lists. xs match { case List() => ... case x :: xs1 => ... } ## Most Important Collections ### Sequences * **`Arrays`** * **`Lists`** * **`Strings`**: you can treat strings like a sequence ### Sets and Maps Consider if you want a mutable or immutable collection. * **`Set`** * **`Map`** * **`TreeSet`**: a sorted set * **`TreeMap`**: a sorted map ### Tuples ### Cool Collections * **`Streams`**: an infinitely long lazily computed collection * **`Vectors`**: similar to a list, but provides more efficient use of elements other than the head * **`Stacks`**: last-in-first-out * **`Queues`**: first-in-first-out * **`Ranges`**: create a range by with `0 to 5`, `0 to 10 by 2`, and `0 until 9`; stored in memory with three numbers (start, stop, and step size) * **`String Builders`**: building strings * **`(Double) Linked Lists`**: mutating a list; optionally double ended ### Iterators Iterators have functions `next` and `hasNext`. ## Implicit Conversions Implicit conversions are conversions that will take place if Scala finds a syntax error. If the conversion removes the error it will use the conversion. Typically you will be defining an implicit function that takes a function and returns a type. So you can later write functions and treat them like that type. The implicit conversion must be in the scope, and only one implicit conversion is tried at a time. ## More For Loops If you want to do a conditional loop within a loop, you could write code similar to this: for (c <- collectionOfCollections; if c.myCondition; e <- c.elements) println(e) To return a collection while using a `for loop` use `yield`. for (i <- 0 until 10; if i % 2) yield i // returns a collection of even numbers You can add many `if` keywords within the `for loop`, and it looks very readable. Separate each statement with a semicolon or a new line. ## Extractors Extractors are used to deconstruct something from its parts. Define an object with an `unapply` method that returns Option[(T, T)]. If it can't deconstruct it, `None` is returned. Regular expressions can help. A simple way to use regular expressions is to put `.r` at the end of a string. val myExpression = "^(.*)$".r * **`findFirstIn`** * **`findAllIn`** * **`findPrefixOf`**: starts at the beginning of the string Use regular expressions like this for (s <- myExpression findAllIn myString) println(s) This works for many variables too, just create different parentheses groups. val myExpression2 = "(a)([^dfw]*)(d)".r Save the groups to three variables. val myExpression2(x, y, z) = myString Do this again, but in a for loop. for (myExpression2(a, b, c) <- myExpression2 findAllIn myString) println(s"found: $a, $b, and $c") ## Futures and Concurrency Futures are easy to use in Scala 1. create the future 2. Call `fut.isComplete` or `fut.value` to find out if it is done. Field `isComplete` returns a boolean and `value` returns an `Option`. So `value` returns `None` if the future is not yet complete. val fut = Future { Thread.sleep(3000); 1 + 1 } Execution starts and it won't be complete until it executes all commands The `value` field can be in state `Try`, `Success`, or `Failure`. Optionally, you can use code like this after creating the future: fut.onComplete { case Success(v) => ... case Failure(ex) => ... } ## GUIs Making a Swing GUI is even simpler in Scala than Java. 1. Extend `SimpleSwingApplication` 2. Define a `new MainFrame` with a title 3. Define your components, such as buttons and labels 4. Assign the field contents a layout type, and place the components contents = new BoxPanel(Orientation.Vertical) { contents += myButton contents += myLabel border = Swing.EmptyBorder(20, 20, 10, 20) } 5. Add events // after the contents section listenTo(myButton) reactions += { case ButtonClicked(b) => ... } ## Scala Scripts as a Shell Script 1. Put this at the top of your file #!/bin/sh exec scala "$0" "$@" !# // put your script below here 2. Set permission with `chmod +x yourFilename` 3. Run the script with `./yourFilename globe` # Forgive Me Missing material on: * **Data structures** * **Functional scala**

COMMENTS


Peter, thanks for helping out with this project!

back to all projects