Functional Scala

Tail calls and tail-call optimizations:

A recursion is called tail call recursion which occurs when a function calls itself as its final operation.

Easiest way to optimize by conversion into a loop. Loops eliminates the potential of stack overflow.

The tail-call optimization won't be applied when a method that calls itself might be overridden in a derived type. The method must be private or final defined in an object or nested in another method(like fact). @tailrec annotation will trigger an error if the compiler can't optimize the annotated method.

Trampoline for tail calls:

is a loop that works through a list of functions calling each one in turn. A kind of recursion where funA -> funB -> funA -> funB .. this type of back and forth recusion can also be converted into a loop using a trampoline.


Functional data structures

1. Lists in fn prg:

Appending elements to a list creates a new list.

val list1 = List("Scala","Haskell")
val list2 = "Some"::"New21"::list1

  • scala also provide immutable list types such as ListBuffer and LinkedList *

2. Maps

  • referred to as hash or dictionary in other languages.
  • do not confuse it with * map * function.
  • there are derived types of immutable and mutable maps which define + and - operators for adding and removing elements. ++ and -- for removing elements defined in Iterators of Pairs.

3. Sets

  • unique lists. doesn't allow duplicates

4. Other data structures in fn prg:

  • Tuples and arrays are used just for convenience but they can be replaced with lists.

Traversing, Mapping, Filtering, Folding and Reducing:

Traversal:(foreach)

List(1,2,3,4,5) foreach { i => println(i) }


  val states = Map("Alabama" -> "Montgomery", 
  								 "Alaska"->"Juneau", 
  								 "Wyoming"-> "Cheyenne")
  states foreach {kv => println(kv._1 +":"+kv._2) }

Mapping: map

val map2 = states map { kv => (kv._1, kv._2.length) }
val lengths = Map[String,Int]() ++ map2

flatMap:

used to flatten a hierarchial data structure, remove empty elements etc. unlike map it may not return a new collection of the same size as the original collection.

Filtering: filter

Other common filtering methods are:
drop(n:Int) - drops the first n elements

dropWhile(p:(A) => Boolean) - returns the longest suffix of this iterable whose first elements does not satisfy the predicate p.

exists(p: (A) => Boolean) - apply a predicate p to all elements of this iterable and return true if there is atleast 1 element yields true. *

filter, find, findIndexOf - returns index or -1*
forall(p: (A) => Boolean):Boolean - apply a predicate p to all elements of this iterable object and return true if the predicate yields true for all elements.

indexOf - returns the index of the 1st occurence of the specified object in the iterable object

partition(p:(A) => Boolean) - partition this iterable in 2 iterables according to a predicate.

sameElements[ B >: A ](that: Iterable[B]): Boolean - checks if the other iterable contains the same elements

take(n:Int): Collection[A] - takes the first n elements of this iterable*

takeWhile(p:(A) => Boolean):Iterable[A] - returns the longest prefix of this iterable whose elements satisfy this predicate*

Folding and reducing:

both are operaions for shrinking a collection down to a smaller collection or a single value.

val list = List(1,2,3,4,5,6)

// can't work on a empty list
list reduceLeft(_ + _) 

// starts with a user supplied value.
list foldLeft(10)(_ * _)

Option container:

Option container supports filter, map, flatMap and other functionally oriented methods that are applied only of if the option isn't empty(i.e if its a Some and not a None).


val someNum = Some(5)
val noneNum = None 

for (option <- List(noneNum, someNum)){
	option.map( n => println(n*5) )
}

Partial functions:

  • are expressions in which not all arguments defined in a fn are supplied as parameters to the function.

Currying

transforms a function that takes multiple parameters into a chain of functions each taking a single parameter.

def cat(s1:String)(s2:String) = s1+s2

We can also convert a fn that take multiple parameters into a curried form using Function.curried

Implicits:

When you have an instance of one type and you need to use it in a context where a different but a similar type is required. In general case you might have an automated conversion mechanism.