02b. Functions (fold), Exceptions - RobertMakyla/scalaWiki GitHub Wiki

Functions

Method operates on an object ( someObject.someMethod() ).

Function does not !!! Functions are just declared in some scope (Functional programming)

Make sure you haven't missed '=' in a function definition. Otherwise return type will be Unit no matter what function returns

def myF = 123

def abs(x: Int) = if (x >= 0) x else -x

If function requires more than one expression, I can use a block, or use ';' :

def silnia(n : Int) = {
   var r = 1
   for (i <- 1 to n) r = r * i
   r                           // last expression of the block is returned
}

The 'return'

(Scala best practices: Avoid using 'return')

It should be used only If I want break functionality of the function. E.g. when I use function to break from the loop.

As long as function is not recursive (calling itself), I don't need to specify return type. Scala determines return type from type of expression to the right of '='.

When function is recursive, I must specify return type:

def silnia(a: Int): Int = if (a==1) 1 else a * silnia(a-1)

def fib1(i: Int): Int = if(i == 1 || i == 2) 1 else fib1(i - 1) + fib1 (i - 2)

def fib2(i: Int): Int = {
   if(i == 1 || i == 2) return 1       // if I don't use 'else'
   fib2(i - 1) + fib2 (i - 2)         //  I must use 'return' to break
}

The default and named arguments (interview)

def decorate(str: String, left: String = "[", right: String = "]") = {
    left + str + right
}

If we don't name it, the defaults are provided naturally - from left:

decorate( "hi", "_")           // _hi]

I can mix unnamed and named. UNNAMED MUST COME FIRST, otherwise error

decorate( "hi", right = "_")   // [hi_   

Named arguments don't need to be in order:

decorate( right = "_", str = "hi") // [hi_  

If we want to skip the name of arg on right side, other args on left must be provided (with name or not)

decorate( str = "hi", "]")          // error
                                   // is "]" left or right ???
decorate( str = "hi", "[", "]")   // ok

Variable Arguments - variable number of arguments

def f (args:Int*) = for (i<- args) yield(i)

def sum(args: Int*) = {      // NOT FUNCTIONAL PROGRAMMING approach
    var result = 0          // IMPERATIVE approach
    for (arg <- args) result += arg
    result
}

sum(1,2,3)     // 6
sum(1 to 3)    // mismatch error: 
               // found Range, required Int (or multiple Int)

sum(1 to 3: _*) // 6   : _* converts Range to a sequence of elemens
                //     : _* is allowed only to convert function arguments

def recSum(s: Int*): Int = 
    if (s.length == 0) 0 else s.head + recSum(s.tail: _*)

def tailRecSum(total: Int = 0, s: Int*): Int = 
    if (s.length == 0) total else tailRecSum(total + s.head, s.tail: _*)

tailRecSum(1 to 3: _*)   // --> 6

Procedures = Functions that don't return any value

If body is enclosed in braces { } without preceding =, then it's a Procedure. The return type is always Unit.

Since it does not return anything, procedure is called only for side effects !!!!!

def frenchItUp(s: String) { print(s"<<${s}>>") }        // procedure

def frenchItUp(s: String)       =   print(s"<<${s}>>")    // function
def frenchItUp(s: String)       = { print(s"<<${s}>>") }  // function
def frenchItUp(s: String): Unit =   print(s"<<${s}>>")    // function
def frenchItUp(s: String): Unit = { print(s"<<${s}>>") }  // function

Lazy Values

(Laziness is not cost free. it requires costly thread safe check before accessing value)

Reading from file immediately, val url created immediately (if no error).

If error, value never created:

val url = scala.io.Source.fromFile("C:/sandbox/git URL for eclipse.txt").mkString

Reading from file just the first time the value is used (if no error).

If error while first use of value (while reading from file), the error will be reproduced each re-use of the value

lazy val url = scala.io.Source.fromFile("C:/sandbox/git URL for eclipse.txt").mkString

Reading from file every time the function url is used, but not immediately - not during function declaration

def url = scala.io.Source.fromFile("C:/sandbox/git URL for eclipse.txt").mkString

Exceptions

Work like in java, but use 'pattern matching' syntax for catch. When we throw an exception and no handler exists, the program terminates - just like in java.

Scala has no checked exceptions (functions or methods never have declared exceptions to throw)

While deciding about the type returned, throw expression is treated as no type 'Nothing' (not even Unit)

val positiveX = if (x >= 0) x else throw new IllegalArgumentException("error")

// returned type is type of 'x', because thrown exception has type Nothing
// supertype of T and Nothing is always T
// cause Nothing is a trait of Any (top of the top)

try/catch

try {
    ...
} catch {
    case _  : MalformedURLException => println("Bad URL")
    case ex : IOException           => ex.printStackTrace()
}
// as in Java, the more general exceptions should come after specific ones

try/finally

try {
    ...
} finally {     // always executed
    ...        // - whether the exception is thrown or not
}

try { ... } catch { ... } finally { ... }
// can also be used like in Java

Excercises:

Type of empty block

val whatTypeOfEmptyBlock = {}    --> Unit=()

Iterating in negative direction(10 to 0):

for( i <- 10 to 0)  println(i)        // KO - must specify step
for( i <- 10 to (0,-1) )  println(i)  // OK - step is -1 

fold

fold left - associates from left to right. First element is assosiated with "start_"

val list = "a" ::"b" ::"c" :: Nil
list.foldLeft("start_")((a,b) => a+b)  //start_abc

("start_" /: list) (_ + _) // the same  

fold right - associates from right to left. Last element is assosiated with "_end"

list.foldRight("_end")((a,b) => a+b)   //abc_end

( list :\ "_end") (_ + _) // the same  

fold (new in scala 2.9) - the order in which the elements are added together is not defined. I.e. the arguments to fold - they are monoid.

list.fold("")((a,b) => a+b)   //abc
list.fold("")(_ + _)         // abc
⚠️ **GitHub.com Fallback** ⚠️