Skip to content

Functional Programming Principles in Scala

Week 1

  • Theory wise, pure imperative programming languages is limited by "Von Neumann" bottleneck

  • Theory:

    1. one or more data types
    2. operations on these types
    3. laws that describe the relationship between values and operations
    4. Normally, a theory does not describe mutations (change something but keep the identity)
  • Theories without Mutation

    1. Pylonomial
    2. String
  • consequence in programming

    • avoid mutations
    • have a power way to abstract operations and functions.
  • The substitution model

    • lambda calculus
    • only to expression don't have a side effect
    • termination
      def loop: Int = loop
      
    • unreduced arguments
  • Call-by-name and call-by-value

    def test(x: Int, y: Int) = x * x
    test(2, 3)
    test(3 + 4, 8)
    test(7, 2 * 4)
    test(3 + 4, 2 * 4)
    

  • CBV, CBN, and termination

    • CBV termination --> CBN termination
    • Not vice versa
    • example
      def first(x: Int, y: Int) = x
      first(7, loop)
      
    • enforce call by name =>
  • Conditional expressions

    • if-else not a statement, just a expression.
  • Value and definitions
    scala> def loop: Boolean = loop && loop
    loop: Boolean
    scala> def x = loop
    x: Boolean
    scala> val x = loop
    java.lang.StackOverflowError
    at .loop(<console>:12)
    
  • Square root with Newton's Method

    def abs(x: Double) = if (x < 0) -x else x
    
    def sqrtIter(guess: Double, x: Double): Double =
    if (isGoodEnough(guess, x)) guess
    else sqrtIter(improve(guess,x), x)
    
    def isGoodEnough(guess: Double, x: Double) =
    abs(guess * guess - x) < 0.001
    
    def improve(guess: Double, x: Double) =
    (guess + x / guess) / 2
    
    def sqrt(x: Double) = sqrtIter(1.0, x)
    

  • Tail recursion: Implementation consideration. If a function calls itself as its last action, the function's stack frame can be reused. This is called tail recursion. Tail recursive function are iterative processes. (anotation: @tailrec)

  • gcd is tail recursion

    def gcd(a: Int, b: Int): Int = {
      if (b == 0) a else gcd(b, a % b)
    }
    

  • factorial is not

    def factorial(n: Int) =
      if (n == 0) 1 else n * factorial(n - 1)
    

  • but we can rewrite factorial in tail recursion form.

    def factorial(n: Int): Int = {
      def loop(acc: Int, n: Int): Int =
        if (n == 0) acc
        else loop(acc * n, n - 1)
      loop(1, n)
    }