Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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.

Daniela Sfregola

May 14, 2021
Tweet

More Decks by Daniela Sfregola

Other Decks in Programming

Transcript

  1. 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"
  2. Everything in Life • Good Parts
 the bits that we

    like • Bad Parts
 the traps to avoid • Ugly Parts
 the parts that we need to live with
  3. Immutability private var i: Int = 0 while (i <

    10) { // do stuff here i = i + 1 } OOP
  4. 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
  5. 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
  6. Is this Pure? import java.time.ZonedDateTime def plusDays(n: Int): ZonedDateTime =

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

    ZonedDateTime.now().plusDays(n) - Referentially Transparent? ⛔ - Total? ✅
  8. Is this Pure? def div(a: Int, b: Int): Int =

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

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

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

    a + b - Referentially Transparent? ✅ - Total? ✅
  12. Purity def foo(s: String): Unit = { // implementation here

    } Impure def foo(s: String): Int = { // implementation here } Pure
  13. Case Class to Array def toArray(product: Product): Array[Any] = {

    product // Product .productIterator // Iterator[Any] .toArray // Array[Any] } FP
  14. 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
  15. 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
  16. 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
  17. JVM and FP do not always go along... OOP version

    is much faster than the FP one!
  18. 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 head :: tail => loop(tail, res :+ f(head)) case Nil => res } } loop(this, res = Nil) }
  19. 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
  20. If you have a really good reason, break the rules!

    ...but keep the ugly bits isolated
  21. Thank You! •Twitter: @DanielaSfregola •"Get Programming with Scala" 
 by

    Manning
 
 35% OFF with code "ctwscalabase21"