2 Getting started

Introduction

Higher-order functions: takes other functions as arguments, and may return functions as the output.

Example Scala Program


In [5]:
// A comment
/* Another comment */
/** A documentation comment */

object MyModular {

    def abs(n: Int): Int = 
        if (n < 0) -n
        else n
    
    private def formatAbs(x: Int) = {
        val msg = "The absolute value of %d is %d"
        msg.format(x, abs(x))
    }
    
    def main(args: Array[String]): Unit = 
        println(formatAbs(-42))
}


Out[5]:
defined object MyModular

In [6]:
MyModular.main(Array())


The absolute value of -42 is 42

2.4 Modular, objects and namespaces

A object whose primary purpose is giving its members a namespace is sometimes called a modular

2.5 Funxiton objects

A example to write loop without mutation:


In [9]:
def factorial(n: Int): Int = {
    def go(n: Int, acc: Int): Int = 
        if (n <=0 ) acc
        else go(n-1, n*acc)

    go(n, 1)
}

def abs(n: Int): Int = {
    if (n <= 0) -n
    else n
}

factorial(10)


Out[9]:
defined function factorial
defined function abs
res8_2: Int = 3628800

In [12]:
// exercise
def formatResult(name: String, n: Int, f: Int => Int) = {
    val msg = "The %s of %d is %s"
    msg.format(name, n, f(n))
}

def main(args: Array[String]): Unit = {
    println(formatResult("absolutie value", -42, abs))
    println(formatResult("factorial", 7, factorial))
    // annonymous functions
    println(formatResult("increment", 7, (x: Int) => x + 1))
}

main(Array())


The absolutie value of -42 is 42
The factorial of 7 is 5040
The increment of 7 is 8
Out[12]:
defined function formatResult
defined function main

In [16]:
// Polymorphic functions (without)
def binarySearch(ds: Array[Double], key: Double): Int = {
    @annotation.tailrec
    def go(low: Int, mid: Int, high: Int): Int = {
        if (low > high) -mid-1
        else {
            val mid2 = (low + high) / 2
            val d = ds(mid2)
            if (d == key) mid2
            else if (d > key) go(low, mid2, mid2-1)
            else go(mid2+1, mid2, high)
        }
    }
    
    go(0, 0, ds.length - 1)
}

// Polymorphic version
def binarySearchP[A](as: Array[A], key: A, gt: (A, A) => Boolean): Int = {
    @annotation.tailrec
    def go(low: Int, mid: Int, high: Int): Int = {
        if (low > high) -mid-1
        else {
            val mid2 = (low + high) / 2
            val a = as(mid2)
            val greater = gt(a, key)
            if (!greater && !gt(a, key)) mid2
            else if (greater) go(low, mid2, mid2-1)
            else go(mid2+1, mid2, high)
        }
    }

    go(0, 0, as.length - 1)
}


Out[16]:
defined function binarySearch
defined function binarySearchP

In [22]:
// exercise2
def isSorted[A](as: Array[A], gt: (A, A) => Boolean): Boolean = {
    @annotation.tailrec
    def go(n: Int): Boolean = {  // Loop to the end,,,,
        if (n >= as.length - 1) true
        else if (gt(as(n), as(n+1))) false
        else go(n+1)
    }
    
    go(0)  // start loop from 0
}


Out[22]:
defined function isSorted

In [19]:
// exercise3
// Implement *partial1* and write down a concrete usage of it
def partial1[A, B, C](a: A, f: (A, B) => C): B => C = (b: B) => f(a, b)


Out[19]:
defined function partial1

In [23]:
// exercise4
// Currying
def curry[A, B, C](f: (A, B) => C): A => (B => C) = 
  a => b => f(a, b)

// exercise5
// Uncurrying
def uncurry[A, B, C](f: A => B => C): (A, B) => C = 
  (a, b) => f(a)(b)

// exercise6
// compose
def compose[A, B, C](f: B => C, g: A => B): A => C = 
  a => f(g(a))


Out[23]:
defined function curry
defined function uncurry
defined function compose

Conclusion

  • some Scala
  • some basic concepts in FP
  • one can often simply 'follow the types' to the correct implementation