In [1]:
// Test scala env.
println("Hello, Scala!")


Hello, Scala!

1.1 The fundamental premise of functional programming

Construct programs using functions that have no side effects.

What is a functions?

A function with input type A and output type B (written in Scala as a single type: A => B ) is a computation which relates every value a of type A to exactly one value b of type B such that b is determined solely by the value of a .

For example, a function intToString having type Int => String will take every integer to a corresponding string. Furthermore, if it really is a function, it will do nothing else.

In other words, a function has no observable effect on the execution of the program other than to compute a result given its inputs; we say that it has no side effects.

We can formalize this idea of pure functions by using the concept of referential transparency (RT): in any program, the expression can be replaced by its result without changing the meaning of the program.

Here is an example of RT and non-RT:


In [2]:
val x = "Hello, Scala!"
val r1 = x.reverse
val r2 = x.reverse


Out[2]:
x: String = "Hello, Scala!"
r1: String = "!alacS ,olleH"
r2: String = "!alacS ,olleH"

In [4]:
// Now replace all the occurrences of the term x with the expression
// referenced by x (its definition), as follows:
val r1 = "Hello, Scala!".reverse
var r2 = "Hello, Scala!".reverse
// The values of r1 and r2 are the same as before, so x was referentially transparent.
// NOTE: x is a function!


Out[4]:
r1: String = "!alacS ,olleH"
r2: String = "!alacS ,olleH"

In [5]:
// Non-RT case
var x = new StringBuilder("Hello")
var y = x.append(", Scala!")
var r1 = y.toString
var r2 = y.toString


Out[5]:
x: StringBuilder = StringBuilder('H', 'e', 'l', 'l', 'o', ',', ' ', 'S', 'c', 'a', 'l', 'a', '!')
y: StringBuilder = StringBuilder('H', 'e', 'l', 'l', 'o', ',', ' ', 'S', 'c', 'a', 'l', 'a', '!')
r1: String = "Hello, Scala!"
r2: String = "Hello, Scala!"

In [6]:
// Let's now see how this side effect breaks RT.
// Replace all y with expression instead.
val x = new StringBuilder("Hello")
val r1 = x.append(", World").toString
var r2 = x.append(", World").toString


Out[6]:
x: StringBuilder = StringBuilder(
  'H',
  'e',
  'l',
  'l',
  'o',
  ',',
  ' ',
  'W',
  'o',
  'r',
  'l',
...
r1: String = "Hello, World"
r2: String = "Hello, World, World"

1.4 Why Functional programming?

FP buys us greater modularity! Why?

A pure function is modular and composable because it separates the logic of the computation itself from "what to do with the result" and "how to obtain the input"; it is a black box.

Functional programmers often speak of implementing programs with a pure core and a thin layer on the outside that handles effects. We will return to this principle again and again throughout the book.x

Here is an example:


In [7]:
case class Player(name: String, score: Int)  // data type Player

def printWinner(p: Player): Unit = 
  println(p.name + " is the winner!")

def declareWinner(p1: Player, p2: Player): Unit =   // takes twp Players
  if (p1.score > p2.score) printWinner(p1)
  else printWinner(p2)


Out[7]:
defined class Player
defined function printWinner
defined function declareWinner

In [8]:
val sue = Player("Sue", 7)
val bob = Player("Bob", 8)
declareWinner(sue, bob)


Bob is the winner!
Out[8]:
sue: Player = Player("Sue", 7)
bob: Player = Player("Bob", 8)

In [10]:
/*
 * In above case, the logic part (compare the score) and the print part
 * are in the same function declareWinner.
 * 
 * 
 * How to reuse the function? How to seperate the logic and print?
 */

// a pure function: takes two Player, return a Player
// it can be re-use easily!
def winner(p1: Player, p2: Player): Player = 
  if (p1.score > p2.score) p1 else p2

// print the winner
def declareWinner(p1: Player, p2: Player): Unit = 
  printWinner(winner(p1, p2))

// List of Players
val players = List(Player("Sue", 7),
                   Player("Bob", 8),
                   Player("Joe", 9))

// reduce the list to just the winner
val p = players.reduceLeft(winner)

// print the winner
printWinner(p)

/* What we learn:
 * any function with side effects can be split into a pure function at
 * the "core" and possibly a pair of functions with side effects; one  
 * on the input side, and one on the output side.
 * /


Joe is the winner!
Out[10]:
defined function winner
defined function declareWinner
players: List[Player] = List(Player("Sue", 7), Player("Bob", 8), Player("Joe", 9))
p: Player = Player("Joe", 9)

Conclusion

  • What is FP?
  • Waht is a function?
  • Waht is referential transparent?
  • Why FP is useful?

In [ ]: