Functionally Insane

Scala combines object-oriented and functional programming in one concise, high-level language. Scala's static types help avoid bugs in complex applications [2]

Notable Syntax

case class Person(id: Int, name: String) {
  def loves(that: Person): String = {
    s"${name} loves ${that.name}"
  }

  def changeName(newName: String): Option[Person] = {
    if(newName.nonEmpty) {
      val personWithNewName = this.copy(name = newName)
      Some(personWithNewName)
    } else {
      None
    }
  }

  def doNothing: Unit = {
    "foo"
  }
}

object Main extends App {
  val juan = new Person(1, "Juan")
  // juan.name = "Foo" // This is a compile time error 
  // Error:(26, 25) reassignment to val juanManuel.name = "Foo"
  val jolene = new Person(2, "Jolene")
  val loveNote = juan.loves(jolene)
  println(loveNote)

  val juanManuel = juan.changeName("Juan Manuel") match {
    case Some(person) => person
    case None => throw new Exception("Can't do that!")
  }
  println(juanManuel)
}

//Juan loves Jolene
//Person(1,Juan Manuel)

Types are defined after the variable/method definition


  def loves(that: Person): String = {
    val foo = s"${name} loves ${that.name}"
  }

Constructors and constructor parameters are simple!

case class Person(id: Int, name: String) {
  // . . .
}

object Main extends App {
  val juan = new Person(1, "Juan")
  juan.id
}

Every expression returns a value


  def loves(that: Person): String = {
    s"${name} loves ${that.name}"
  }

  def changeName(newName: String): Option[Person] = {
    if(newName.nonEmpty) {
      val personWithNewName = this.copy(name = newName)
      Some(personWithNewName)
    } else {
      None
    }
  }

  def doNothing: Unit = {
    "foo"
  }

Re-assignment of values will cause a compile time error


  val juan = new Person(1, "Juan")
  juan.name = "Foo" // This is a compile time error 
  // Error:(26, 25) reassignment to val juan.name = "Foo"
  

Pattern matching is a powerful feature

case class Person(id: Int, name: String) {
  def changeName(newName: String): Option[Person] = {
    if(newName.nonEmpty) {
      val personWithNewName = this.copy(name = newName)
      Some(personWithNewName)
    } else {
      None
    }
  }

  def doNothing: Unit = {
    "foo"
  }
}

object Main extends App {
  val juan = new Person(1, "Juan")
  val juanManuel = juan.changeName("Juan Manuel") match {
    case Some(person) => person
    case None => throw new Exception("Can't do that!")
  }
}

What is Functional Programming?

What is Functional Programming?

Is a way to construct programs only using:

Use Pure Functions
Immutable Values
1
2

So what is a pure function?

  1. Depends only on its input parameters and its internal algorithm
  2. Has no side effects

This means no interaction with the outside world!

How can you sniff impure functions?

class Foo {
  def getFoo: Int = {
    // ...
  }
}
class Bar {
  def doBar(Type param): Unit = {
    // Do something
  }
}

Functions that don't take any parameters

Functions that don't return anything

Side
Effects
Include

Side

effects

include

  • Modifying an existing variable
  • Printing to the screen (including CLI)
  • Making API calls
  • Writing to a file
  • Reading from a file
  • Setting a field on an object
  • Throwing exceptions
  • Printing
  • Returning null values!
  • Reading from a database
  • . . .

Consider for a moment

What programming would be like without the ability to do these things? [1]

Functional programming restricts how we write programs, but it is not a restriction on what programs can express.

Learning how to express all your programs without side effects , including the ones that do IO, handle errors, and modify data, can be extremely beneficial to increase modularity, simplify testing, increase reuse, enable parallelization, and are easier to understand [1]

Pure FP

Core

Impure Outer

Layer

What "pure" functional applications look like

 

 

Immutability

What is an immutable value?

  1. Is a value that never changes, once a value has been assigned it is permanent

But, Why???

The best FP Code reads like algebra. Take the following program:

  val a = f(x)
  val b = g(a)
  val c = h(b)
  return c

We could replace the value of 'c' with the result of 'h(b)' and the meaning of the program will remain unchanged

val c = 42
return c

Immutable Values

With immutable values, we have a strong guarantee that when we replace c = h(g(f(x)) with it's result 42, the meaning of the program will not be affected[1]

Easier
To
Reason

Functional programming is easier to reason about

object AsyncExample extends App {
  // Nums is a Java array which can be modified in place!
  val nums = (-100000 to 100000).reverse.toArray

  // Sum all the numbers in the array while we sort the array
  val task1 = Future {nums.sum}
  val task2 = Future {util.Arrays.sort(nums); nums}

  // printVal is a helper function
  task1.onComplete(printVal)
  task2.onComplete(printVal)

  Seq(task2, task1).foreach(Await.result(_, 50.millis))
}

Functional programming is easier to reason about

# first run
This is the reversed array: 100000, 99999, 99998 ...  -99998, -99999, -100000
This is the ordered array: -100000, -99999, -99998 ... 99998, 99999, 100000
This is the sum: 21788228

# second run
# ...
This is the sum: 25184250

# third run
# ...
This is the sum: 599994

# fourth run
# ...
This is the sum: 0

Functional programming is easier to reason about

object AsyncImmutableExample extends App {

  val nums = (-1000000 to 1000000).reverse
  printVal(nums)

  val task1 = Future {nums.sum}
  val task2 = Future {nums.sorted}

  task1.onComplete(printVal)
  task2.onComplete(printVal)

  Seq(task2, task1).foreach(Await.result(_, 50.millis))
}

Functional programming is easier to reason about

# first run
This is the reversed array: 1000000, 999999, 999998 ...  -999998, -999999, -1000000
This is the ordered array: -1000000, -999999, -999998 ... 999998, 999999, 1000000
This is the sum: 0

# second run
# ...
This is the sum: 0

# third run
# ...
This is the sum: 0

# fourth run
# ...
This is the sum: 0

Higher
Order
Functions

What are Higher order functions (hof)?

It is simply a function that can take other functions as parameters, or return functions

Assigning functions to vals

In this instance, the filter method of the collections class is a HOF as it takes a function and applies it to every element of the in the range the isEven function

 

The filter method is defined as:

val isEven = (i: Int) => i % 2 == 0
val myRange = (1 to 10)
val evenNumbers = myRange.filter(i => isEven(i))
// Write it more concisely
val evenNumbers = myRange.filter(isEven)
// This will result in
(2, 4, 6, 8, 10)
def filter(p: (T) => Boolean): List[T]

Currying

Currying

It is a technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions [3]

  def sum(a: Int)(b: Int): Int = {
    a + b
  }

  val addOne: Int => Int = sum(1)
  val addTwo: Int => Int = sum(2)
  val addTen: Int => Int = sum(10)

  println(addOne(1))  // 2
  println(addTwo(1))  // 3
  println(addTen(1))  // 11

This syntactic sugar is equivalent to

val sum: Int => (Int => Int) = {
    a => { 
        b => a + b 
    }
}

In this context we say that sum is a partially applied function!

A partial application is the process of applying a function to some of its arguments. A partially-applied function gets returned for later use [1]

Custom Control Structure

def myWhile(condition: => Boolean)(codeBlock: => Unit): Unit = {
  while(condition) {
    codeBlock
  }
}


var i = 0
myWhile(i < 10) {
  println(i)
  i += 1 
}

Wrapping it all up

def wrapper(tag: String)(codeBlock: => String): String = {
   
  s"<$tag>" + codeBlock + s"</$tag>"
  
}
def wrapperWithParameters(tag: String)(parameters: Seq[(String, String)])(codeBlock: => String): String = {
  s"<$tag " +
    parameters.map{ case (key, value) => s"$key=$value"}.mkString(" ") + " >" +
    codeBlock +
  s"</$tag>"
}
val htmlWrapper = wrapperWithParameters("html") _
val divWrapper = wrapper("div")(_)
val strongWrapper = wrapper("strong")(_)
val preWrapper = wrapper("pre")(_)

Functional HTML Wrappers

val html: String = htmlWrapper(Seq("key" -> "value")) {
  divWrapper {
    preWrapper {
      strongWrapper {
        "My Awesome functional website!"
      }
    }
  }
}
<html key=value >
  <div>
    <pre>
      <strong>My Awesome functional website!</strong>
    </pre>
  </div>
</html>

Functional HTML Wrappers

Conclusion

Conclusion

  • Challenge yourself to write code using only "immutable" variables
  • Functional programming principles will enable you to write better and safer programs
  • We didn't cover it, but pure functions are easier to test
  • Functional programs can be more modular
  • Let me know if  you want to learn more about Scala!

References

Functionally Insane

By Juan Manuel Torres

Functionally Insane

  • 829