Thursday, October 27, 2016

Quick Guide To Scala

This cheat sheet is a modified version of the #progfun forum, credits to Laurent Poulain. Most of the things are copied from there. A few things are changed here and there and a few more examples are added to the original version.

Evaluation Rules

  • Call by value: evaluates the function arguments before calling the function
  • Call by name: evaluates the function first, and then evaluates the arguments if need be
Evaluation Rules






def example = 2      // evaluated when called
val example = 2      // evaluated immediately
lazy val example = 2 // evaluated once when needed
def square(x: Double)    // call by value
def square(x: => Double) // call by name
def myFct(bindings: Int*) = { ... } // bindings is a sequence of int, containing a varying # of arguments

Higher order functions

These are functions that take a function as a parameter or return functions.
Higher Order Functions












// sum() returns a function that takes two integers and returns an integer 
def sum(f: Int => Int): (Int, Int) => Int = { 
  def sumf(a: Int, b: Int): Int = {...} 
  sumf 
}
// same as above. Its type is (Int => Int) => (Int, Int) => Int 
def sum(f: Int => Int)(a: Int, b: Int): Int = { ... }
// Called like this
sum((x: Int) => x * x * x)          // Anonymous function, i.e. does not have a name 
sum(x => x * x * x)                 // Same anonymous function with type inferred
def cube(x: Int) = x * x * x 
sum(x => x * x * x)(1, 10) // sum of cubes from 1 to 10
sum(cube)(1, 10)           // same as above

Currying

Converting a function with multiple arguments into a function with a single argument that returns another function.
Currying
def f(a: Int, b: Int): Int // uncurried version (type is (Int, Int) => Int)
def f(a: Int)(b: Int): Int // curried version (type is Int => Int => Int)

Classes
Classes
class MyClass(x: Int, y: Int) {           // Defines a new type MyClass with a constructor 
  require(y > 0, "y must be positive")    // precondition, triggering an IllegalArgumentException if not met 
  def this (x: Int) = { ... }             // auxiliary constructor  
  def nb1 = x                             // public method computed every time it is called 
  def nb2 = y 
  private def test(a: Int): Int = { ... } // private method 
  val nb3 = x + y                         // computed only once 
  override def toString =                 // overridden method 
      member1 + ", " + member2
  }
new MyClass(1, 2) // creates a new object of type
this references the current object, assert(<condition>) issues AssertionError if condition is not met. See scala.Predef for require, assume and assert.

Operators

myObject myMethod 1 is the same as calling myObject.myMethod(1)
Operator (i.e. function) names can be alphanumeric, symbolic (e.g. x1, *, +?%&, vector_++, counter_=)
The precedence of an operator is determined by its first character, with the following increasing order of priority:
Operator Precedence
(all letters)
|
^
&
< >
= !
:
+ -
* / %
(all other special characters)
The associativity of an operator is determined by its last character: Right-associative if ending with :, Left-associative otherwise.
Note that assignment operators have lowest precedence. (Read Scala Language Specification 2.9 sections 6.12.3, 6.12.4 for more info).

Class Hierarchies

Class Hierarchy
abstract class TopLevel {     // abstract class 
  def method1(x: Int): Int   // abstract method 
  def method2(x: Int): Int = { ... } 
}
class Level1 extends TopLevel { 
  def method1(x: Int): Int = { ... } 
  override def method2(x: Int): Int = { ...} // TopLevel's method2 needs to be explicitly overridden 
}
object MyObject extends TopLevel { ... } // defines a singleton object. No other instance can be created

To create an runnable application in Scala:
Runnable Application in Scala
//first example
//very similar to Java
object Hello { 
  def main(args: Array[String]) = println("Hello world") 
}
  
//second example
//this will work too
object Hello extends App {
  println("Hello World")
}

Class Organization
  • Classes and objects are organized in packages (package myPackage).
  • They can be referenced through import statements (import myPackage.MyClass, import myPackage._, import myPackage.{MyClass1, MyClass2}, import myPackage.{MyClass1 => A})
  • They can also be directly referenced in the code with the fully qualified name (new myPackage.MyClass1)
  • All members of packages scala and java.lang as well as all members of the object scala.Predef are automatically imported.
  • Traits are similar to Java interfaces, except they can have non-abstract members:trait Planar { ... } class Square extends Shape with Planar
  • General object hierarchy:
    scala.Any base type of all types. Has methods hashCode and toString that can be overridden
    - scala.AnyVal base type of all primitive types. (scala.Double, scala.Float, etc.)
    - scala.AnyRef base type of all reference types. (alias of java.lang.Object, supertype of java.lang.String,scala.List, any user-defined class)
    - scala.Null is a subtype of any scala.AnyRef (null is the only instance of type Null), and scala.Nothing is a subtype of any other type without any instance.

Type Parameters

Similar to Java generics. These can apply to classes, traits or functions.
Type Parameters
class MyClass[T](arg1: T) { ... } 
new MyClass[Int](1) 
new MyClass(1)   // the type is being inferred, i.e. determined based on the value arguments

Collections
Scala defines several collection classes:

Base Classes

  • Iterable (collections you can iterate on)
  • Seq (ordered sequences)
  • Set
  • Map (lookup data structure)

Immutable Collections

  • List (linked list, provides fast sequential access)
  • Stream (same as List, except that the tail is evaluated only on demand)
  • Vector (array-like type, implemented as tree of blocks, provides fast random access)
  • Range (ordered sequence of integers with equal spacing)
  • String (Java type, implicitly converted to a character sequence, so you can treat every string like a Seq[Char])
  • Map (collection that maps keys to values)
  • Set (collection without duplicate elements)

Mutable Collections

  • Array (Scala arrays are native JVM arrays at runtime, therefore they are very performant)
  • Scala also has mutable maps and sets; these should only be used if there are performance issues with immutable types
Examples of Collection

val fruitList = List("apples", "oranges", "pears")
// Alternative syntax for lists
val fruit = "apples" :: ("oranges" :: ("pears" :: Nil)) // parens optional, :: is right-associative
fruit.head   // "apples"
fruit.tail   // List("oranges", "pears")
val empty = List()
val empty = Nil
val nums = Vector("louis", "frank", "hiromi")
nums(1)                     // element at index 1, returns "frank", complexity O(log(n))
nums.updated(2, "helena")   // new vector with a different string at index 2, complexity O(log(n))
val fruitSet = Set("apple", "banana", "pear", "banana")
fruitSet.size    // returns 3: there are no duplicates, only one banana
val r: Range = 1 until 5 // 1, 2, 3, 4
val s: Range = 1 to 5    // 1, 2, 3, 4, 5
1 to 10 by 3  // 1, 4, 7, 10
6 to 1 by -2  // 6, 4, 2
val s = (1 to 6).toSet
s map (_ + 2) // adds 2 to each element of the set
val s = "Hello World"
s filter (c => c.isUpper) // returns "HW"; strings can be treated as Seq[Char]
// Operations on sequences
val xs = List(...)
xs.length   // number of elements, complexity O(n)
xs.last     // last element (exception if xs is empty), complexity O(n)
xs.init     // all elements of xs but the last (exception if xs is empty), complexity O(n)
xs take n   // first n elements of xs
xs drop n   // the rest of the collection after taking n elements
xs(n)       // the nth element of xs, complexity O(n)
xs ++ ys    // concatenation, complexity O(n)
xs.reverse  // reverse the order, complexity O(n)
xs updated(n, x)  // same list than xs, except at index n where it contains x, complexity O(n)
xs indexOf x      // the index of the first element equal to x (-1 otherwise)
xs contains x     // same as xs indexOf x >= 0
xs filter p       // returns a list of the elements that satisfy the predicate p
xs filterNot p    // filter with negated p
xs partition p    // same as (xs filter p, xs filterNot p)
xs takeWhile p    // the longest prefix consisting of elements that satisfy p
xs dropWhile p    // the remainder of the list after any leading element satisfying p have been removed
xs span p         // same as (xs takeWhile p, xs dropWhile p)
List(x1, ..., xn) reduceLeft op    // (...(x1 op x2) op x3) op ...) op xn
List(x1, ..., xn).foldLeft(z)(op)  // (...( z op x1) op x2) op ...) op xn
List(x1, ..., xn) reduceRight op   // x1 op (... (x{n-1} op xn) ...)
List(x1, ..., xn).foldRight(z)(op) // x1 op (... (    xn op  z) ...)
xs exists p    // true if there is at least one element for which predicate p is true
xs forall p    // true if p(x) is true for all elements
xs zip ys      // returns a list of pairs which groups elements with same index together
xs unzip       // opposite of zip: returns a pair of two lists
xs.flatMap f   // applies the function to all elements and concatenates the result
xs.sum         // sum of elements of the numeric collection
xs.product     // product of elements of the numeric collection
xs.max         // maximum of collection
xs.min         // minimum of collection
xs.flatten     // flattens a collection of collection into a single-level collection
xs groupBy f   // returns a map which points to a list of elements
xs distinct    // sequence of distinct entries (removes duplicates)
x +: xs  // creates a new collection with leading element x
xs :+ x  // creates a new collection with trailing element x
// Operations on maps
val myMap = Map("I" -> 1, "V" -> 5, "X" -> 10)  // create a map
myMap("I")      // => 1 
myMap("A")      // => java.util.NoSuchElementException 
myMap get "A"   // => None
myMap get "I"   // => Some(1)
myMap.updated("V", 15)  // returns a new map where "V" maps to 15 (entry is updated)
                        // if the key ("V" here) does not exist, a new entry is added
// Operations on Streams
val xs = Stream(1, 2, 3)
val xs = Stream.cons(1, Stream.cons(2, Stream.cons(3, Stream.empty))) // same as above
(1 to 1000).toStream // => Stream(1, ?)
x #:: xs // Same as Stream.cons(x, xs)
         // In the Stream's cons operator, the second parameter (the tail)
         // is defined as a "call by name" parameter.
         // Note that x::xs always produces a List