Daniela Sfregola
May 14, 2021
110

# ScalaBase 2021- FP: the Good, the Bad, the Ugly

You are about to fall in love with Functional Programming, if not already. You are going to learn the good parts that are going to make your day-to-day life easier. But since nobody is perfect - not even FP -, you are also going to see its bad and ugly parts, and you'll discover how to deal with them: from learning challenges to performance issues on the JVM.

May 14, 2021

## Transcript

1. FP
The Good,
@DanielaSfregola

Scalabase 2021

2. Hellooooo
• Software Engineer
living in London UK

• Former Java Developer

• Scala Lover ❤

• OS Maintainer

• Author of
"Get Programming with Scala"
by Manning

35% OFF with code "ctwscalabase21"

3. • Rome, Cinecitta'
23 December 1966

• Director
Sergio Leone

• Music
Ennio Morricone

4. Everything in Life
• Good Parts
the bits that we like

the traps to avoid

• Ugly Parts
the parts that we need to live with

5. FP: The Good

6. Increased
Productivity

7. Immutability
private var i: Int = 0
while (i < 10) {
// do stuff here
i = i + 1
}
OOP

8. Immutability
private var i: Int = 0
while (i < 10) {
// do stuff here
i = i + 1
}
OOP
(0 until 10).foreach { i =>
// do stuff here
}
FP

9. Immutability
private var i: Int = 0
while (i < 10) {
i = i * 2
println(i)
i = i + 1
}
OOP
(0 until 10).foreach { i =>
println(i * 2)
}
FP

10. RTness
Totality
Purity
+
=

11. Referential Transparent
same input same output
Totality
always return a value

12. Is this Pure?
import java.time.ZonedDateTime
def plusDays(n: Int): ZonedDateTime =
ZonedDateTime.now().plusDays(n)
- Referentially Transparent?
- Total?

13. Is this Pure?
import java.time.ZonedDateTime
def plusDays(n: Int): ZonedDateTime =
ZonedDateTime.now().plusDays(n)
- Referentially Transparent? ⛔
- Total? ✅

14. Is this Pure?
def div(a: Int, b: Int): Int = a / b
- Referentially Transparent?
- Total?

15. Is this Pure?
def div(a: Int, b: Int): Int = a / b
- Referentially Transparent? ✅
- Total? ⛔

16. Is this Pure?
def sum(a: Int, b: Int): Int = a + b
- Referentially Transparent?
- Total?

17. Is this Pure?
def sum(a: Int, b: Int): Int = a + b
- Referentially Transparent? ✅
- Total? ✅

18. Purity
def foo(s: String): Unit = {
// implementation here
}
Impure

19. Purity
def foo(s: String): Unit = {
// implementation here
}
Impure
def foo(s: String): Int = {
// implementation here
}
Pure

20. Purity
Composability

21. Puzzle Coding!
from https://scalameta.org/metals/
Metals

23. Learning Curve

24. Functional
Programming
!=
Categorical
Programming

25. You do not need to know
Category Theory
to write good
Functional code

EASY!

a Monoid
in the Category of
Endofunctors

Burritos

30. The importance of
talks and tutorials
for Beginners

31. The Rock Star
Developer Syndrome

32. The Rock Star Developer

not a "Rock Star"

34. FP: The Ugly

35. Knowing when to
break the rules

36. Case Class to Array
def toArray(product: Product): Array[Any] = {
product // Product
.productIterator // Iterator[Any]
.toArray // Array[Any]
}
FP

37. Case Class to Array
def toArray(product: Product): Array[Any] = {
product // Product
.productIterator // Iterator[Any]
.toArray // Array[Any]
}
def toArray(product: Product): Array[Any] = {
val size = product.productArity
val arr = new Array[Any](size)
var i = 0
while (i < size) {
arr(i) = product.productElement(i)
i = i + 1
}
arr
}
FP
OOP

38. Benchmarking*
import com.danielasfregola.randomdatagenerator.RandomDataGenerator._
def avgRunTime[A](f: => A, n: Int = 1000): Double = {
val start = Instant.now.toEpochMilli
(0 until n).foreach(_ => f)
val end = Instant.now.toEpochMilli
val diff = end - start
diff / n.toDouble
}
case class BigExample(f1: String, f2: Int, f3: Long, f4: Char, f5: String,
f6: String, f7: Int, f8: Long, f9: Char, f10: String,
f11: String, f12: Int, f13: Long, f14: Char, f15: String,
f16: String, f17: Int, f18: Long, f19: Char, f20: String,
f21: String, f22: Int, f23: Long, f24: Char, f25: String,
f26: String, f27: Int, f28: Long, f29: Char, f30: String)
val big = random[BigExample]
avgRunTime(toArrayFP(big))
avgRunTime(toArrayOOP(big))
*not a serious benchmarking, executed on my laptop

39. Benchmarking*
import com.danielasfregola.randomdatagenerator.RandomDataGenerator._
def avgRunTime[A](f: => A, n: Int = 1000): Double = {
val start = Instant.now.toEpochMilli
(0 until n).foreach(_ => f)
val end = Instant.now.toEpochMilli
val diff = end - start
diff / n.toDouble
}
case class BigExample(f1: String, f2: Int, f3: Long, f4: Char, f5: String,
f6: String, f7: Int, f8: Long, f9: Char, f10: String,
f11: String, f12: Int, f13: Long, f14: Char, f15: String,
f16: String, f17: Int, f18: Long, f19: Char, f20: String,
f21: String, f22: Int, f23: Long, f24: Char, f25: String,
f26: String, f27: Int, f28: Long, f29: Char, f30: String)
val big = random[BigExample]
avgRunTime(toArrayFP(big))
avgRunTime(toArrayOOP(big))
*not a serious benchmarking, executed on my laptop
toArrayOOP avg execution: 0.002 ms
toArrayFP avg execution: 0.028 ms

40. JVM and FP
do not always go along...
OOP version
is much faster than the
FP one!

41. My ideal implementation of
map function for List[A]
final override def map[B](f: A => B): List[B] = {
@tailrec
def loop(l: List[A], res: List[B]): List[B] = {
l match {
case Nil => res
}
}
loop(this, res = Nil)
}

42. The real implementation of
map function for List[A]*
final override def map[B](f: A => B): List[B] = {
if (this eq Nil) Nil else {
val h = new ::[B](f(head), Nil)
var t: ::[B] = h
var rest = tail
while (rest ne Nil) {
val nx = new ::(f(rest.head), Nil)
t.next = nx
t = nx
rest = rest.tail
}
releaseFence()
h
}
}
*scala 2.13.0

43. If you have a really good reason,
break the rules!
...but keep the ugly bits isolated

44. Thank You!