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

Let’s Blend some Data Structures

Let’s Blend some Data Structures

How parametric polymorphism and Algebra can solve a real world problem

https://www.meetup.com/en-AU/AnnArborFP/events/243678261/

Filippo Vitale

October 24, 2017
Tweet

More Decks by Filippo Vitale

Other Decks in Programming

Transcript

  1. Map( "k1" -> List(11, 12, 13), "k2" -> List(21, 22,

    23), ) key value key value key value
  2. 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)))))
  3. “Mutability is an optimisation – perhaps premature” Seq Set Map

    base trait for mutable & immutable implementations
  4. Blending Appending Lists List(1, 2, 3) ++ List(4, 5, 6)

    == List(1, 2, 3, 4, 5, 6) http://www.scala-lang.org/api/current/#scala.collection.immutable.List http://docs.scala-lang.org/overviews/collections/seqs.html
  5. Blending Adding Sets 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)
  6. Immutable Collection Hierarchy Traversable Seq Set Map List HashSet HashMap

    http://docs.scala-lang.org/tutorials/FAQ/collections.html
  7. Blending Adding Maps Map() ++ Map("a" -> 1) == Map("a"

    -> 1) http://www.scala-lang.org/api/current/#scala.collection.immutable.Map http://docs.scala-lang.org/overviews/collections/maps.html
  8. Blending Adding Maps Map("a" -> Set(1, 2)) ++ Map("a" ->

    Set(2, 3)) == ??? http://www.scala-lang.org/api/current/#scala.collection.immutable.Map http://docs.scala-lang.org/overviews/collections/maps.html
  9. 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 What is the result of “blending” those Maps?
  10. 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 What is the result of “blending” those Maps?
  11. 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 What is the result of “blending” those Maps?
  12. 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 }
  13. 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 }
  14. 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
  15. 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
  16. 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 mb.foldLeft(ma) { ... }
  17. 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
  18. 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
  19. 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) } }
  20. “When you get used to immutable data, ya kinda forget

    how to use mutable data in a sensible way.” – Jessica Kerr
  21. (ma /: mb) { case (result,(k, v)) => result +

    (k -> { result.get(k) match { case Some(vr) => vr ++ v case None => v } }
  22. (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
  23. (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)) }
  24. (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)) }
  25. 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]]] = ???
  26. 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 + ??? // { ??? => { ??? } } }
  27. Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]] trait Blendable[A] { def blend(ma:

    A, mb: A): A } new Blendable[...] { // ad-hoc polymorphism def blend(ma: ..., mb: ...): ... = ??? }
  28. What blended “as expected”? List using the ++ binary operator

    Set using the ++ binary operator 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)
  29. (List, ++) (Set, ++) (Int, +) (String, +) ("ab" blend

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

    Blendable[Map[String, Set[Int]]].blend(ma, mb) Map[String, Set[Int]]
  31. What is a Semigroup? Michael Barr, Charles Wells - Category

    Theory for Computing Science Semigroups “A semigroup is a set S together with an associative binary operation m: S × S → S”
  32. Closure Property http://en.wikipedia.org/wiki/Closure_(mathematics) trait Semigroup[T] { def op(a: T, b:

    T): T } For all a, b in T, the result of the operation a ⋅ b is also in T: ∀a, b ∈ T : a∙b ∈ T
  33. http://en.wikipedia.org/wiki/Closure_(mathematics) trait Semigroup[T] { def op(a: T, b: T): T

    } def op(a: Boolean, b: Boolean): Boolean def op(a: Int, b: Int): Boolean ✓ ✘ Closure Property
  34. Associative Property http://en.wikipedia.org/wiki/Associative_property trait Semigroup[T] { def op(a: T, b:

    T): T } For all a, b, and c in T, the equation (a ⋅ b) ⋅ c = a ⋅ (b ⋅ c) holds: ∀a, b, c ∈ T : (a∙b)∙c = a∙(b∙c) (a op b) op c == a op (b op c)
  35. What is a Semigroup? Michael Barr, Charles Wells - Category

    Theory for Computing Science Semigroups “A semigroup is a set S together with an associative binary operation m: S × S → S”
  36. 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)
  37. 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"
  38. /** * 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)) }
  39. 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.
  40. 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.”
  41. 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.” Adapted from: Functional Programming in Scala - Part 3 - Chapter 10 Monoids
  42. Map("a" -> Set(1, 2)) |+| Map("a" -> Set(2, 3)) res:

    Map[…] = Map(a -> Set(1, 2, 3)) ✓
  43. 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)))))
  44. “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