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)(110// sum of cubes from 1 to 10
sum(cube)(110)           // 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 = 
  private def test(a: Int): Int = { ... } // private method 
  val nb3 = x + y                         // computed only once 
  override def toString =                 // overridden method 
      member1 ", " + member2
  }
new MyClass(12// 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.Doublescala.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 = (1 to 6).toSet
s map (_ 2// adds 2 to each element of the set
val = "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(123)
val xs = Stream.cons(1, Stream.cons(2, Stream.cons(3, Stream.empty))) // same as above
(1 to 1000).toStream // => Stream(1, ?)
#:: 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