02b. Functions (fold), Exceptions - RobertMakyla/scalaWiki GitHub Wiki
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
}
(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
}
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
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
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
(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
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 {
case _ : MalformedURLException => println("Bad URL")
case ex : IOException => ex.printStackTrace()
}
// as in Java, the more general exceptions should come after specific ones
try {
...
} finally { // always executed
... // - whether the exception is thrown or not
}
try { ... } catch { ... } finally { ... }
// can also be used like in Java
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 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