$30 off During Our Annual Pro Sale. View Details »

LSUG 2019 - FP: The Good, The Bad and The Ugly

LSUG 2019 - FP: The Good, The Bad and 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

November 21, 2019
Tweet

More Decks by Daniela Sfregola

Other Decks in Programming

Transcript

  1. FP
    The Good,
    the Bad and the Ugly
    @DanielaSfregola

    LSUG - November 2019

    View Slide

  2. Hellooooo
    • Italian living in London UK

    • Former Java Developer

    • Scala Lover

    View Slide

  3. • Rome, Cinecitta' 

    23 December 1966

    • Director

    Sergio Leone

    • Music

    Ennio Morricone

    View Slide

  4. 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

    View Slide

  5. FP: The Good

    View Slide

  6. Increased
    Productivity

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

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

    View Slide

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

    View Slide

  12. Purity
    Composability

    View Slide

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

    View Slide

  14. FP: The Bad

    View Slide

  15. Learning Curve

    View Slide

  16. Functional
    Programming

    != 

    Categorical
    Programming

    View Slide

  17. You do not need to know
    Category Theory
    to write good 

    Functional code

    View Slide

  18. ...but Monads are
    EASY!

    View Slide

  19. A Monad is just
    a Monoid
    in the Category of
    Endofunctors

    View Slide

  20. Monads are like

    Burritos
    from https://twitter.com/monadburritos/status/915624691829587968

    View Slide

  21. Is Lasagna a Monad?

    View Slide

  22. The importance of
    talks and tutorials
    for Beginners

    View Slide

  23. The Rock Star
    Developer Syndrome

    View Slide

  24. The Rock Star Developer
    from https://twitter.com/honeypotio/status/689811823281991681

    View Slide

  25. Be a Leader
    not a "Rock Star"

    View Slide

  26. FP: The Ugly

    View Slide

  27. Knowing when to
    break the rules

    View Slide

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

    View Slide

  29. 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

    View Slide

  30. 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

    View Slide

  31. 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
    toArrayFP avg execution: 0.028 ms
    toArrayOOP avg execution: 0.002 ms

    View Slide

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

    View Slide

  33. 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)
    }

    View Slide

  34. 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

    View Slide

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

    View Slide

  36. Scala
    FP OOP

    View Slide

  37. Scala is
    Perfect

    View Slide

  38. Scala is
    Perfect
    not

    View Slide

  39. Not even Scala is perfect...
    • Slow Compiler

    • pipelining compilation by Jason Zaugg

    • zinc by Eugene Yokota

    • Tooling

    • bloop by Jorge Vicente Cantero

    • metals by Ólafur Páll Geirsson

    View Slide

  40. Thank You!
    •Twitter: @DanielaSfregola

    •Blog: danielasfregola.com

    •Book: Get Programming with Scala

    by Manning

    View Slide