will it blend? - scalasyd february 2015

96
Will it Blend? February 2015 @filippovitale

Upload: filippo-vitale

Post on 17-Aug-2015

5 views

Category:

Software


2 download

TRANSCRIPT

Page 1: Will it Blend? - ScalaSyd February 2015

Will it Blend?

February 2015

@filippovitale

Page 2: Will it Blend? - ScalaSyd February 2015
Page 3: Will it Blend? - ScalaSyd February 2015

valuevalue

value

Map Set

key value

key value

key value

Listv v v v

Page 4: Will it Blend? - ScalaSyd February 2015

List

Page 5: Will it Blend? - ScalaSyd February 2015

Immutable Collection Hierarchy

Traversable Seq List

http://docs.scala-lang.org/tutorials/FAQ/collections.html

Page 6: Will it Blend? - ScalaSyd February 2015

Immutable Collection Hierarchy

Traversable Seq List

http://docs.scala-lang.org/tutorials/FAQ/collections.html

def ++[B] (that:Traversable[B])

:Traversable[B]

Page 7: Will it Blend? - ScalaSyd February 2015

Blending Lists

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

Page 8: Will it Blend? - ScalaSyd February 2015

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

def ++[B](that: Traversable[B]): List[B]

Returns a new list containing the elements from the left hand operand followed by the elements from the right hand operand.

Page 9: Will it Blend? - ScalaSyd February 2015

Set

Page 10: Will it Blend? - ScalaSyd February 2015

Immutable Collection Hierarchy

Traversable Seq

Set

List

HashSet

http://docs.scala-lang.org/tutorials/FAQ/collections.html

Page 11: Will it Blend? - ScalaSyd February 2015

Blending Sets

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

Page 12: Will it Blend? - ScalaSyd February 2015

Blending Adding Sets

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

http://www.scala-lang.org/api/current/#scala.collection.immutable.Set

http://docs.scala-lang.org/overviews/collections/sets.html

def ++[B](that: Traversable[B]): Set[B]

Returns a new set containing the elements from the left hand operand followed by the elements from the right hand operand.

Page 13: Will it Blend? - ScalaSyd February 2015

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)

Page 14: Will it Blend? - ScalaSyd February 2015
Page 15: Will it Blend? - ScalaSyd February 2015
Page 16: Will it Blend? - ScalaSyd February 2015

Map

Page 17: Will it Blend? - ScalaSyd February 2015

Immutable Collection Hierarchy

Traversable Seq

Set

Map

List

HashSet

HashMap

http://docs.scala-lang.org/tutorials/FAQ/collections.html

Page 18: Will it Blend? - ScalaSyd February 2015

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

def ++[B](that: Traversable[(A, B)]): Map[A, B]

Returns a new map containing the elements from the left hand operand followed by the elements from the right hand operand.

Page 19: Will it Blend? - ScalaSyd February 2015

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

def ++[B](that: Traversable[(A, B)]): Map[A, B]

Returns a new map containing the elements from the left hand operand followed by the elements from the right hand operand.

Page 20: Will it Blend? - ScalaSyd February 2015

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?

Page 21: Will it Blend? - ScalaSyd February 2015

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

1:

2:

3: Map("a" -> Set(2, 3))

4:

5:

What is the result of “blending” those Maps?

Page 22: Will it Blend? - ScalaSyd February 2015

Map("a" -> Set(1, 2)) ??? Map("a" -> Set(2, 3))

Map("a" -> Set(1, 2, 3))

Page 23: Will it Blend? - ScalaSyd February 2015
Page 24: Will it Blend? - ScalaSyd February 2015
Page 25: Will it Blend? - ScalaSyd February 2015
Page 26: Will it Blend? - ScalaSyd February 2015

Map with values as Set (of 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)

val ma: Map[String, Set[Int]]

val mb: Map[String, Set[Int]]

Page 27: Will it Blend? - ScalaSyd February 2015

Map with values as Set (of Int)

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

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

Page 28: Will it Blend? - ScalaSyd February 2015

Map with values as Set (of Int)

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

“a” Set(1, 2, 3)

Page 29: Will it Blend? - ScalaSyd February 2015

def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]): Map[String, Set[Int]] = { for ((k, v) <- mb) { ??? } }

Page 30: Will it Blend? - ScalaSyd February 2015

def blend(ma: Map[String, Set[Int]], mb: Map[String, Set[Int]]): Map[String, Set[Int]] = { val result = mutable.Map() ++ ma for ((k, v) <- mb) { ??? } result.toMap}

Page 31: Will it Blend? - ScalaSyd February 2015

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}

Page 32: Will it Blend? - ScalaSyd February 2015

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}

Page 33: Will it Blend? - ScalaSyd February 2015

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() ++ mamb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v}result.toMap

Page 34: Will it Blend? - ScalaSyd February 2015

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() ++ mamb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v}result.toMap

mb.foldLeft(ma) { ... }

Page 35: Will it Blend? - ScalaSyd February 2015

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() ++ mamb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v}result.toMap

Page 36: Will it Blend? - ScalaSyd February 2015

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() ++ mamb foreach { case (k, v) => if (result.contains(k)) result += k -> (result(k) ++ v) else result += k -> v}result.toMap

Page 37: Will it Blend? - ScalaSyd February 2015

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

Page 38: Will it Blend? - ScalaSyd February 2015

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

Page 39: Will it Blend? - ScalaSyd February 2015

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

Page 40: Will it Blend? - ScalaSyd February 2015

(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) // // }}

Page 41: Will it Blend? - ScalaSyd February 2015

(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) // }}

Page 42: Will it Blend? - ScalaSyd February 2015

(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)}}

Page 43: Will it Blend? - ScalaSyd February 2015

(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

http://en.wikipedia.org/wiki/Catamorphism

“Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire”

Page 44: Will it Blend? - ScalaSyd February 2015

(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

http://en.wikipedia.org/wiki/Catamorphism

“Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire”

Page 45: Will it Blend? - ScalaSyd February 2015

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

val result = mutable.Map() ++ mamb foreach { case (k, v) => result += (k -> result.get(k).cata(_ ++ v, v))}

Page 46: Will it Blend? - ScalaSyd February 2015
Page 47: Will it Blend? - ScalaSyd February 2015
Page 48: Will it Blend? - ScalaSyd February 2015
Page 49: Will it Blend? - ScalaSyd February 2015

What if we changeData Structure?

Page 50: Will it Blend? - ScalaSyd February 2015

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

Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]]

Page 51: Will it Blend? - ScalaSyd February 2015

def blend(ma: Map[String, Map[Int, Set[Int]]], mb: Map[String, Map[Int, Set[Int]]]): Map[String, Map[Int, Set[Int]]] = (ma /: mb) { case (result, (k, v)) => result + ??? // { ??? => { ??? } } }

Map[String, Set[Int]] Map[String, Map[Int, Set[Int]]]

Page 52: Will it Blend? - ScalaSyd February 2015

trait Blendable[A] { def blend(ma: A, mb: A): A}

new Blendable[...] { def blend(ma: ..., mb: ...): ... = ???}

Page 53: Will it Blend? - ScalaSyd February 2015

trait Blendable[A] { def blend(ma: A, mb: A): A}

new Blendable[...] { def blend(ma: ..., mb: ...): ... = ???}

x10 Developer

Page 54: Will it Blend? - ScalaSyd February 2015

What is the meaning of “Blending”?

(in my world)

Page 55: Will it Blend? - ScalaSyd February 2015

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)

Page 56: Will it Blend? - ScalaSyd February 2015

(List, ++)(Set, ++)

(1 blend 2) == ???

What about Int?

Page 57: Will it Blend? - ScalaSyd February 2015

(List, ++)(Set, ++)

(Int, +)

(1 blend 2) == 1 + 2 == 3

What about Int?

Page 58: Will it Blend? - ScalaSyd February 2015

(List, ++)(Set, ++)

(Int, +)(String, +)("ab" blend "cd") == ("ab" + "cd") == "abcd"

String

Page 59: Will it Blend? - ScalaSyd February 2015

(List, ++)(Set, ++)

(Int, +)(String, +)

(Map[...], Blendable[...].blend)

Blendable[Map[String, Set[Int]]].blend(ma, mb)

Map[String, Set[Int]]

Page 60: Will it Blend? - ScalaSyd February 2015

What canMathematicians

tell us here?

Page 61: Will it Blend? - ScalaSyd February 2015

Warning

Category Theory ahead

Page 62: Will it Blend? - ScalaSyd February 2015

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”

Page 63: Will it Blend? - ScalaSyd February 2015

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

Page 64: Will it Blend? - ScalaSyd February 2015

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

Page 65: Will it Blend? - ScalaSyd February 2015

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)

Page 66: Will it Blend? - ScalaSyd February 2015

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”

Page 67: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup

Page 68: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup

import scalaz.std.set._

Page 69: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup implicit def setSemigroup[A]:Semigroup[Set[A]] =

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

Page 70: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup implicit def setSemigroup[A]:Semigroup[Set[A]] =

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

op

Page 71: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup import scalaz.syntax.semigroup._

import scalaz.std.list._

Set(1, 2) |+| Set(2, 3)

res0: scala.collection.immutable.Set[Int] = Set(1, 2, 3)

Page 72: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup import scalaz.syntax.semigroup._

import scalaz.std.list._

List(1, 2) |+| List(3, 4)

res0: scala.collection.immutable.List[Int] = List(1, 2, 3, 4)

Page 73: Will it Blend? - ScalaSyd February 2015

Scalaz and Semigroup import scalaz.syntax.semigroup._

import scalaz.std.string._

"a" |+| "b" |+| "c"

res0: String = abc

Page 74: Will it Blend? - ScalaSyd February 2015

/*** 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))}

Page 75: Will it Blend? - ScalaSyd February 2015

import scalaz.scalacheck.ScalazProperties._

semigroup.laws[Int].check

+ semigroup.associative: OK, passed 100 tests.

Page 76: Will it Blend? - ScalaSyd February 2015

import scalaz.scalacheck.ScalazProperties._

semigroup.laws[String].checksemigroup.laws[Set[Int]].checksemigroup.laws[List[String]].checksemigroup.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.

Page 77: Will it Blend? - ScalaSyd February 2015

import scalaz.scalacheck.ScalazProperties._

semigroup.laws[Map[String, Set[Int]]].check

Page 78: Will it Blend? - ScalaSyd February 2015

import scalaz.scalacheck.ScalazProperties._

semigroup.laws[Map[String, Set[Int]]].check

+ semigroup.associative: OK, passed 100 tests.

Page 79: Will it Blend? - ScalaSyd February 2015

Map("a" -> Set(1, 2)) |+| Map("a" -> Set(2, 3))

Page 80: Will it Blend? - ScalaSyd February 2015

Map("a" -> Set(1, 2)) |+| Map("a" -> Set(2, 3))

res0: ...immutable.Map[...] = Map(a -> Set(1, 2, 3))

Page 81: Will it Blend? - ScalaSyd February 2015

Map("a" -> Set(1, 2)) |+| Map("a" -> Set(2, 3))

“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

Page 82: Will it Blend? - ScalaSyd February 2015

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)))))

Page 83: Will it Blend? - ScalaSyd February 2015

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

Page 84: Will it Blend? - ScalaSyd February 2015

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

res0: ...immutable.Map[...] = Map(a -> 3, b -> 4)

Page 85: Will it Blend? - ScalaSyd February 2015

1.some |+| 2.someres0: Option[Int] = Some(3)

1.some |+| none[Int]res1: Option[Int] = Some(1)

Page 86: Will it Blend? - ScalaSyd February 2015

implicit def optionMonoid[A: Semigroup]: ... def append(f1: Option[A], f2: => Option[A]) = (f1, f2) match { case (Some(a1), Some(a2)) => Some(Semigroup[A].append(a1, a2)) case (Some(a1), None) => f1 case (None, sa2 @ Some(a2)) => sa2 case (None, None) => None }}

Page 87: Will it Blend? - ScalaSyd February 2015

Customised Semigroup

case class MeetupOrganiser(name: String, meetup: String, yowCommunityAward: Int)

val jed = MeetupOrganiser("Jed", "scalasyd", 1)val mark = MeetupOrganiser("Mark", "scalasyd", 0)val leo = MeetupOrganiser("Leonardo", "clj-syd", 0)

Page 88: Will it Blend? - ScalaSyd February 2015

Customised Semigroup

case class MeetupOrganiser(name: String, meetup: String, yowCommunityAward: Int)

implicit val meetupOrganiserSemigroup = new Semigroup[MeetupOrganiser] { def append(f1: MeetupOrganiser, f2: => MeetupOrganiser): MeetupOrganiser = if (f1.yowCommunityAward >= f2.yowCommunityAward) f1 else f2}

Page 89: Will it Blend? - ScalaSyd February 2015

Customised Semigroup

implicit val meetupOrganiserArbitrary: Arbitrary[MeetupOrganiser] = Arbitrary(for { n <- arbitrary[String] m <- arbitrary[String] r <- arbitrary[Int] } yield MeetupOrganiser(n, m, r))

implicit val meetupOrganiserEqual: Equal[MeetupOrganiser] = Equal.equal(_ == _)

semigroup.laws[MeetupOrganiser].check+ semigroup.associative: OK, passed 100 tests.

Page 90: Will it Blend? - ScalaSyd February 2015

Customised Semigroup

mark |+| leores0: MeetupOrganiser = MeetupOrganiser(Mark,scalasyd,0)

mark |+| leo |+| jedres1: MeetupOrganiser = MeetupOrganiser(Jed,scalasyd,1)

NonEmptyList(mark, leo, jed).suml1res2: MeetupOrganiser = MeetupOrganiser(Jed,scalasyd,1)

Page 91: Will it Blend? - ScalaSyd February 2015
Page 93: Will it Blend? - ScalaSyd February 2015

Map[String, Set[Int]]

Page 94: Will it Blend? - ScalaSyd February 2015
Page 95: Will it Blend? - ScalaSyd February 2015

Thanks!

February 2015

@filippovitale

Page 96: Will it Blend? - ScalaSyd February 2015

Questions?

February 2015

@filippovitale