Wednesday, April 27, 2016

SCALA - Scala for Java developers notes

Resources:
This course on Pluralsight
http://scalaforfunandprofit.com/why-use-fsharp/

http://docs.scala-lang.org/style/

History
Started in 2003 as research project in Switzerland.
Research was headed by Martin Odersky - an author of Java Generics and Java compiler.


Scala was adapted by:

Scala is for the future Software Development and there is non too much experienced devs
http://insights.dice.com/2014/04/04/employers-cant-find-enough-scala-talent/

Pure functions 
Is associated with objects and work without a side-effects (or mutation).
Define fixed variable - vow.

Higher order functions 
Functions are "first citizens" in Scala. This why functions could:

  • receive other functions in its parameters
  • return functions
  • to be stored for later execution



These allow a Functional composition - powerful technique in FP.

Scala class structure

ScalaDoc 

Scala supports Lambdas (or Anonymous functions).
You can pass function literals as an argument and use a function signature as an argument in method signature.



Function literals (i.e. tuples):
Function literals are anonymous -- they don't have a name by default, but you can give them a name by binding them to a variable. A function literal is defined like so: (a:Int, b:Int) => a + b. You can bind them to variables: val add = (a:Int, b:Int) => a + b 
add(1, 2) // Result is 3. 

Function signature:
http://scalaforfunandprofit.com/posts/function-signatures/

// function signature 1
int -> int -> int
This function takes two int parameters and returns another, so presumably it is some sort of mathematical function such as addition, subtraction, multiplication, or exponentiation.
// function signature 2
int -> unit
This function takes an int and returns a unit, which means that the function is doing something important as a side-effect. Since there is no useful return value, the side effect is probably something to do with writing to IO, such as logging, writing to a file or database, or something similar.
// function signature 3
unit -> string
This function takes no input but returns a string, which means that the function is conjuring up a string out of thin air! Since there is no explicit input, the function probably has something to do with reading (from a file say) or generating (a random string, say).

Infix notation
Use of the method without Dot notation. e.g:
Dot a.+(b)
Infix a + b 

CFR - another java decompiler
http://benf.org/other/cfr

Getters and Setters generation rules


COMPANION OBJECT
It is a way to create a singleton from a Class.
Should have the same Name as its Class.
Even private methods will be accessible from its Class.
If a method does not depends on class fields we can call it Function. (more like a static methods in Java).
Functions belong to a singleton Object rather to its Class.
OBJECT will usually be used for Factory methods: e.g. creating an instance of class:
def apply(name: String, surname: String) = new Customer(name, surname)
val andre = Customer.apply("Andre", "Paniov")

APPLY is a Default method of a Class. If it is ommited the class will call it implicitly: e.g. creating an instance of class:
val andre = Customer("Andre", "Paniov")//the same as Customer.apply(...)

CONSTRUCTOR
Constructor is defined by a class declaration with its argument.
You can declare Class private (forcing clients to use Fabric method of Companion Object) like this:
class Customer private (var forename: String...

WHEN USE CLASS, TRAITS, ABSTRACT CLASS



















SCALA GENERICS




CURLY BRACE RULE for ONE argument parameter (by-name parameters)
By-Name parameters. They are not lazy. They are evaluated each time they encountered.
scala curly brace rule


Before:
















In the middle:














After:

















Even Better:


















CURRYING parameters functions
def add(x:Int, y:Int) = x + y
def add(x:Int) = (y:Int) => x + y
def add(x:Int)(y:Int) = x + y

===================================================
def add(x:Int, y:Int) = x + y
val addCurried = Function.curried(add _)
 
add(1, 2)   // 3
addCurried(1)(2)   // 3

===================================================
def add(x:Int)(y:Int) = x + y
val addUncurried = Function.uncurried(add _)
 
add(3)(4)  // 7
addUncurried(3, 4)  // 7

===================================================
def add(x:Int, y:Int, z:Int) = x + y + z
 
val addFive = add(5, _:Int, _:Int)
addFive(3, 1)    // 9

==================================================
def add(x:Int, y:Int, z:Int) = x + y + z
 
val addFive = (a:Int, b:Int) => add(5, a, b)
addFive(3, 1)    // 9

scala curring parameters



Pattern MATCHING
http://scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html


Constructor pattern
Allows you to match on the arguments used to construct an object.
CASE key word enable class with generated useful methods like hashCode, toString etc.







Type Queries (without casting or instanceof)






EXTRACTOR METHOD - UNAPPLY





Representation Independence 
The decoupling between patterns and the data types that they work against:







Pattern Guard











MAPPING (Map is Transforming function)
def age(birthYear: Int) = {
  Calendar.getInstance().get(Calendar.YEAR) - birthYear
}

def main(args: Array[String]) {
  val years = List(1960, 1972, 1981, 2004)

  val result = years.map(age)
  val result1 = years.map(year => Calendar.getInstance().get(Calendar.YEAR) - year)
  val result2 = years.map(Calendar.getInstance().get(Calendar.YEAR) - _)

  println(result)//the same result is for result1, result2
}

How to implement Map function in Scala:
class Mappable[A](elements: A*) {
  /*def map[B](f: Function1[A, B]): List[B] = {    ???  }*/
  //shorthand syntax  def map[B](f: A => B): List[B] = {
    val result = collection.mutable.MutableList[B]()
    elements.foreach {
      result += f.apply(_)
    }
    result.toList
  }
}

object Example {
  def main(args: Array[String]) {
    val numbers = List(1, 2, 54, 4)
    val mappable = new Mappable(numbers:_*)
    val result = mappable.map(_ * 2)
    println(result)
  }
}

FLATMAP vs MAP
FlatMap is a function of one-to-many transformation. It takes one value and generates several results.
Flattents List[List[a]] to List[A]










def ages(birthYear: Int): List[Int] = {
  val today = Calendar.getInstance().get(Calendar.YEAR)
  List(today - 1 - birthYear, today - birthYear, today + 1 - birthYear)
}

def main(args: Array[String]) {
  val years = List(1945, 1961, 1972, 1994)

  println(years.map(ages))
  //List(List(70, 71, 72), List(54, 55, 56), List(43, 44, 45), List(21, 22, 23))

  println(years.flatMap(ages))
  //List(70, 71, 72, 54, 55, 56, 43, 44, 45, 21, 22, 23)}

How to implement FlatMap function in Scala:
class FlattMappable[A](elements: A*) {
  def flatMap[B](f: A => List[B]): List[B] = {
    val result = collection.mutable. MutableList[B]()
    elements.foreach {
      f.apply(_).foreach {
        result += _
      }
    }
    result.toList
  }
}

object FlatMapExample {
  def oddNumbersTo(end: Int): List[Int] = {
    val odds = collection.mutable.MutableList[Int]()
    for (i <- 0 to end) {
      if (i % 2 != 0) odds += i
    }
    odds.toList
  }

  def main(args: Array[String]) {
    val mappable = new FlattMappable(1, 2, 10)
    val result = mappable.flatMap(oddNumbersTo)
    println(result)
    //List(1, 1, 1, 3, 5, 7, 9)  }
}

MONADS
Simple definition: Monad is something that has map and flatMap functions.
Class Option is an example of using Monads. It avoids to return Null and us map and flatMap.



















All Monads are Functors, and map comes from Functors


override def findOrNull(name: String): Customer = {
  for (customer <- customers) {
    if (customer.name == name)
      return customer
  }
  null}
override def find(name: String): Option[Customer] = {
  for (customer <- customers) {
    if (customer.name == name)
      return Some(customer)
  }
  None
}

object Example {
  val customers = new CustomerSet()
  val basketValue: Option[Double] = customers.find("Albert").map(customer => customer.total)

  //if this is None it will throw an Exception  val x: Double = basketValue.get

  //this how to avoid exception on None  val y: Double = customers.find("Missing") match {
    case Some(customer) => customer.total
    case None => 0D  }

  //even much better w/o matching pattern  val z: Double = basketValue.getOrElse(0D)

  val usCustomers = Set("Albert", "Beatriz", "Carol", "Dave", "Erin")  usCustomers.map(customers.find(_).map(_.total)).flatten.sum
  usCustomers.flatMap(name => customers.find(name)).map(_.total)
}

For-comprehension in depth



IMPROVED BY FLATMAP



FOR de-sugared by FOREACH


FOR (NESTED) de-sugared


FOR YIELD de-sugared


FOR YIELD (NESTED) de-sugared


What to Learn Next













No comments: