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?
- Depends only on its input parameters and its internal algorithm
- 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?
- 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
- 883