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

Polimorfismo cosa – Milano Scala Group – September 2015

Polimorfismo cosa – Milano Scala Group – September 2015

Uno sguardo a 3 comuni strutture dati presenti nelle librerie standard di Scala ponendo l'attenzione sul polimorfismo parametrico offerto da alcune operazioni ed
il loro utilizzo con implementazioni sia in stile imperativo/procedurale sia funzionale.

A seguire un esempio pratico di utilizzo del polimorfismo ad-hoc per operazioni con strutture dati di type composti ed uno sguardo su come trarre benefici nell'utilizzare
in questi casi librerie come Scalaz.

Filippo Vitale

September 15, 2015
Tweet

More Decks by Filippo Vitale

Other Decks in Programming

Transcript

  1. Settembre 2015 @filippovitale Polimorfismo parametrico, polimorfismo su misura e polimorfismo

    cosa?
  2. None
  3. None
  4. 3 semplici Data Structure

  5. Seq Set Map package scala.collection

  6. Seq Set Map package scala.collection v v v v

  7. Seq Set Map package scala.collection valueA valueB valueC

  8. Seq Set Map package scala.collection key value key value key

    value
  9. Seq Set Map package scala.collection base trait per implementazioni mutable

    e immutable
  10. package scala.collection Seq Set Map package scala.collection.mutable ArrayBuffer HashSet HashMap

  11. package scala.collection.mutable ArrayBuffer HashSet HashMap “Mutability is an optimisation –

    perhaps premature”
  12. package scala.collection Seq Set package scala.collection.immutable Map List HashSet HashMap

    package scala.collection.mutable ArrayBuffer HashSet HashMap http://docs.scala-lang.org/tutorials/FAQ/collections.html
  13. package scala.collection.immutable List HashSet HashMap Immutabilità implica: - equational reasoning

    - sharing with referential integrity - thread safety - …
  14. “When you get used to immutable data, ya kinda forget

    how to use mutable data in a sensible way.” – Jessica Kerr
  15. package scala.collection Traversable Seq Set Map

  16. Quali metodi offre Traversable?

  17. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString
  18. isEmpty size hasDefiniteSize ++ map flatMap filter remove partition groupBy

    foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Metodi offerti da Traversable def map[B](f: A => B)
  19. isEmpty size hasDefiniteSize ++ map flatMap filter remove partition groupBy

    foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Metodi offerti da Traversable def foreach(f: (A) => Unit): Unit def map[B](f: A => B)
  20. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString
  21. isEmpty size hasDefiniteSize ++ map flatMap filter remove partition groupBy

    foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Metodi offerti da Traversable def foldLeft[B](z: B)(f: (B, A) => B): B
  22. isEmpty size hasDefiniteSize ++ map flatMap filter remove partition groupBy

    foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Metodi offerti da Traversable def foldLeft[B](z: B)(f: (B, A) => B): B def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
  23. isEmpty size hasDefiniteSize ++ map flatMap filter remove partition groupBy

    foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Metodi offerti da Traversable def foldLeft[B](z: B)(f: (B, A) => B): B def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
  24. Theorems for free! – http://ttic.uchicago.edu/~dreyer/course/papers/wadler.pdf

  25. Parametricity – http://yowconference.com.au/slides/yowlambdajam2014/Morris-ParametricityTypesAreDocumentation.pdf

  26. Unire Strutture Dati

  27. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString
  28. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString def ++[B](that: Traversable[B]): Traversable[B]
  29. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString def ++[B](that: Traversable[B]): Traversable[B] Traversable Seq List
  30. List(1, 2, 3) ++ List(4, 5, 6) == ???

  31. List(1, 2, 3) ++ List(4, 5, 6) == List(1, 2,

    3, 4, 5, 6)
  32. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString def ++[B](that: Traversable[B]): Traversable[B] Traversable Set HashSet
  33. Set(1, 2, 3) ++ Set(4, 5, 6) == ???

  34. Set(1, 2, 3) ++ Set(4, 5, 6) == Set(5, 1,

    6, 2, 3, 4)
  35. Set(1, 2, 3) ++ Set(4, 5, 6) == Set(5, 1,

    6, 2, 3, 4) Set(1, 2) ++ Set(2, 3) == Set(1, 2, 3)
  36. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString def ++[B](that: Traversable[B]): Traversable[B] Traversable Map HashMap
  37. Map("a" -> 1) ++ Map("b" -> 2) == Map("a" ->

    1, "b" -> 2)
  38. E in casi più complessi?

  39. Map[String, Set[Int]]

  40. Map[String, Set[Int]] “a” Set(1, 2) “key b” Set(4, 7, 5)

    “key c” Set(9, 4) “a” Set(2, 3) “key c” Set(3, 4) “key d” Set(5, 6)
  41. Map("a" -> Set(1, 2)) ++ Map("a" -> Set(2, 3)) ==

    ???
  42. Map("a" -> Set(1, 2)) ++ Map("a" -> Set(2, 3)) ==

    ??? 1: Map("a" -> Set(1, 2)) 2: Map("a" -> Set(1, 2, 3)) 3: Map("a" -> Set(2, 3)) 4: RuntimeException 5: Compiler Error
  43. Map("a" -> Set(1, 2)) ++ Map("a" -> Set(2, 3)) ==

    ??? 1: 2: 3: Map("a" -> Set(2, 3)) 4: 5:
  44. Map("a" -> Set(1, 2)) ??? Map("a" -> Set(2, 3)) Map("a"

    -> Set(1, 2, 3))
  45. Metodi offerti da Traversable isEmpty size hasDefiniteSize ++ map flatMap

    filter remove partition groupBy foreach reduceRightOpt head headOption tail last lastOption init take drop slice takeWhile forall exists count find foldLeft /: foldRight :\ reduceLeft reduceLeftOpt reduceRight dropWhile span splitAt toArray toList toIterable toSeq toStream sortWith mkString toString Seq Set Map ✔ ✔ ✘
  46. Quando vuoi un lavoro fatto bene…

  47. “a” Set(1, 2) “a” Set(2, 3) “a” Set(1, 2) ++

    Set(2, 3)
  48. “a” Set(1, 2) “a” Set(2, 3) “a” Set(1, 2, 3)

  49. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

  50. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

    = { mb foreach { case (k, v) => ??? } }
  51. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

    = { val result = mutable.Map() ++ ma mb foreach { case (k, v) => ??? } result.toMap }
  52. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

    = { val result = mutable.Map() ++ ma mb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v } result.toMap }
  53. Implementazione con Map immutable

  54. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

    = { } (ma /: mb) { case (result,(k, v)) => if (result.contains(k)) result + (k -> (result(k) ++ v)) else result + (k -> v) } val result = mutable.Map() ++ ma mb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v } result.toMap
  55. def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]) : Map[String, Set[Int]]

    = { (ma /: mb) { case (result,(k, v)) => if (result.contains(k)) result + (k -> (result(k) ++ v)) else result + (k -> v) } }
  56. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } }
  57. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) }}
  58. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) // .some(_ ++ v).none(v) }}
  59. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) // .some(_ ++ v).none(v) // .fold(v)(_ ++ v) }}
  60. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) // .some(_ ++ v).none(v) // .fold(v)(_ ++ v) // .cata(_ ++ v, v) }} “FP with Bananas, Lenses, Envelopes and Barbed Wire” – http://citeseer.ist.psu.edu/viewdoc/summary?doi=10.1.1.41.125 http://en.wikipedia.org/wiki/Catamorphism
  61. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) // .some(_ ++ v).none(v) // .fold(v)(_ ++ v) // .cata(_ ++ v, v) }} http://stackoverflow.com/questions/5328007/why-doesnt-option-have-a-fold-method
  62. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } // .map(_ ++ v).getOrElse(v) // .some(_ ++ v).none(v) // .fold(v)(_ ++ v) // .cata(_ ++ v, v) }} http://stackoverflow.com/questions/5328007/why-doesnt-option-have-a-fold-method
  63. (ma /: mb) { case (result, (k, v)) => result

    + (k -> result.get(k).cata(_ ++ v, v)) } mb foreach { case (k, v) => result += (k -> result.get(k).cata(_ ++ v, v)) }
  64. (ma /: mb) { case (result, (k, v)) => result

    + (k -> result.get(k).cata(_ ++ v, v)) } mb foreach { case (k, v) => result += (k -> result.get(k).cata(_ ++ v, v)) }
  65. None
  66. E se volessimo unire due mappe con type diversi?

  67. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]]

  68. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] def blend(ma: Map[String, Set[Int]], mb:

    Map[String, Set[Int]]) : Map[String, Set[Int]] = ??? def blend(ma: Map[String, Map[Int, Set[Int]]], mb: Map[String, Map[Int, Set[Int]]]) : Map[String, Map[Int, Set[Int]]] = ???
  69. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] (ma /: mb) { case

    (result, (k, v)) => result + (k -> result.get(k).cata(_ ++ v, v)) } (ma /: mb) { case (result, (k, v)) => result + ??? // { ??? => { ??? } } }
  70. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] trait Blendable[A] { def blend(ma:

    A, mb: A): A }
  71. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] trait Blendable[A] { def blend(ma:

    A, mb: A): A } new Blendable[...] { def blend(ma: ..., mb: ...): ... = ??? }
  72. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] trait Blendable[A] { def blend(ma:

    A, mb: A): A } new Blendable[...] { def blend(ma: ..., mb: ...): ... = ??? } x10 Developer
  73. Cosa intendiamo veramente per “blend”

  74. List utilizzando l’operatore binario ++ Set utilizzando l’operatore binario ++

    List(1, 2, 3) ++ List(4, 5, 6) == List(1, 2, 3, 4, 5, 6) Set(1, 2) ++ Set(2, 3) == Set(1, 2, 3)
  75. (List, ++) (Set, ++) (1 blend 2) == ???

  76. (List, ++) (Set, ++) (Int, +) (1 blend 2) ==

    1 + 2 == 3
  77. (List, ++) (Set, ++) (Int, +) (String, +) ("ab" blend

    "cd") == ("ab" + "cd") == "abcd"
  78. (List, ++) (Set, ++) (Int, +) (String, +) (Map[...], Blendable[...].blend)

    Blendable[Map[String, Set[Int]]].blend(ma, mb)
  79. Cosa potrebbe consigliarci un Matematico?

  80. WARNING Algebra ahead ☣

  81. https://it.wikipedia.org/wiki/Semigruppo “Un semigruppo è un insieme S munito di una

    operazione binaria associativa m: S × S → S”
  82. https://it.wikipedia.org/wiki/Propriet%C3%A0_di_chiusura Proprietà di chiusura ≝ ∀a, b ∈ T :

    a∙b ∈ T Per ogni a, b in T, il risultato dell’operazione a⋅b è in T: trait Semigroup[T] { def op(a: T, b: T): T } def op(a: Boolean, b: Boolean): Boolean def op(a: Int, b: Int): Boolean ✓ ✘
  83. https://it.wikipedia.org/wiki/Associativit%C3%A0 Legge Associativa ≝ ∀a, b, c ∈ T :

    (a∙b)∙c = a∙(b∙c) Ogni a, b e c in T soddisfano (a∙b)∙c = a∙(b∙c) trait Semigroup[T] { def op(a: T, b: T): T } ((a op b) op c) == (a op (b op c))
  84. https://it.wikipedia.org/wiki/Semigruppo “Un semigruppo è un insieme S munito di una

    operazione binaria associativa m: S × S → S”
  85. Scalaz e Semigruppi

  86. None
  87. import scalaz.std.set._ implicit def setSemigroup[A]:Semigroup[Set[A]] = new Semigroup[Set[A]] { def

    append(f1: Set[A], f2: => Set[A]) = f1 ++ f2 }
  88. implicit def setSemigroup[A]:Semigroup[Set[A]] = new Semigroup[Set[A]] { def append(f1: Set[A],

    f2: => Set[A]) = f1 ++ f2 } op
  89. import scalaz.syntax.semigroup._ import scalaz.std.list._ List(1, 2) |+| List(3, 4) res:

    List[Int] = List(1, 2, 3, 4) import scalaz.syntax.semigroup._ import scalaz.std.set._ Set(1, 2) |+| Set(2, 3) res: Set[Int] = Set(1, 2, 3)
  90. import scalaz.syntax.semigroup._ import scalaz.std.anyVal._ 1 |+| 2 |+| 3 res:

    Int = 6 import scalaz.syntax.semigroup._ import scalaz.std.string._ "a" |+| "b" |+| "c" res: String = "abc"
  91. /** * A semigroup in type F must satisfy two

    laws: * * - '''closure''': `∀ a, b in F, append(a, b)` is also in `F`. * - '''associativity''': `∀ a, b, c` in `F`, the equation * `append(append(a, b), c) = append(a, append(b , c))` holds. */ trait SemigroupLaw { def associative(f1: F, f2: F, f3: F)(implicit F: Equal[F]): Boolean = F.equal(append(f1, append(f2, f3)), append(append(f1, f2), f3)) }
  92. import scalaz.scalacheck.ScalazProperties._ import scalaz.std.anyVal._ semigroup.laws[Int].check + semigroup.associative: OK, passed 100

    tests.
  93. semigroup.laws[String].check semigroup.laws[Set[Int]].check semigroup.laws[List[String]].check semigroup.laws[Map[Int, Int]].check + semigroup.associative: OK, passed 100

    tests. + semigroup.associative: OK, passed 100 tests. + semigroup.associative: OK, passed 100 tests. + semigroup.associative: OK, passed 100 tests.
  94. La nostra Map[_, Set[Int]] è un semigruppo?

  95. Map("a" -> 1, "b" -> 4) |+| Map("a" -> 2)

    res: Map[…] = Map(a -> 3, b -> 4) “Some data structures form interesting semigroups as long as the types of the elements they contain also form semigroups.”
  96. import scalaz.scalacheck.ScalazProperties._ semigroup.laws[Map[String, Set[Int]]].check + semigroup.associative: OK, passed 100 tests.

  97. Map("a" -> Set(1, 2)) |+| Map("a" -> Set(2, 3)) res:

    Map[…] = Map(a -> Set(1, 2, 3)) “adattato” da: Functional Programming in Scala - Part 3 - Chapter 10 Monoids ✓
  98. Ma nel mio codebase non ho Map così semplici…

  99. Map("a" -> Map("aa" -> Map("aaa" -> Map("aaaa" -> List(1, 3),

    "aaab" -> List(2, 4))))) |+| Map("a" -> Map("aa" -> Map("aaa" -> Map("aaaa" -> List(5, 7), "aaab" -> List(6, 8))))) Map(a->Map(aa->Map(aaa->Map(aaaa->List(1, 3, 5, 7), aaab->List(2, 4, 6, 8)))))
  100. Benchmarking

  101. “Experience indicates that nearly everybody has the wrong idea about

    the real bottlenecks in his programs” – Donald Knuth Computer programming as an art (1974) – http://dl.acm.org/citation.cfm?id=361612
  102. Map[String, Set[Int]]

  103. None
  104. Settembre 2015 @filippovitale $ tail -f domande