Last active
August 29, 2015 14:27
-
-
Save sshark/a8babe2ad553843b0cc3 to your computer and use it in GitHub Desktop.
Using monoids with Map and Option as value. Does it make sense to have Option as value insteadof the actual value because the Map can return Option. This code does not compile
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // following http://inoio.de/blog/2014/07/19/type-class-101-semigroup/ | |
| trait Semigroup[A] { | |
| def append(a: A, b: A): A | |
| } | |
| trait Monoid[A] extends Semigroup[A] { | |
| def zero: A | |
| } | |
| implicit val intSemigroup = new Semigroup[Int] { | |
| def append(a: Int, b: Int) = a + b | |
| } | |
| implicit val intMonoid = new Monoid[Int] { | |
| def append(a: Int, b: Int) = a + b | |
| def zero = 0 | |
| } | |
| object Semigroup { | |
| def apply[A](implicit F: Semigroup[A]) = F | |
| } | |
| object Monoid { | |
| def apply[A](implicit F: Monoid[A]) = F | |
| } | |
| Semigroup.apply[Int].append(1, 2) | |
| Semigroup[Int].append(1, 2) | |
| Monoid[Int].append(2,3) | |
| trait SemigroupSyntax[A] { | |
| def self: A | |
| def F: Semigroup[A] | |
| def |+|(b: A): A = append(b) | |
| // alias for append | |
| def append(b: A): A = F.append(self, b) | |
| } | |
| trait MonoidSyntax[A] extends SemigroupSyntax[A] { | |
| def zero: A | |
| def F: Monoid[A] | |
| } | |
| implicit def ToMonoidOps[A: Monoid](a: A): MonoidSyntax[A] = | |
| new MonoidSyntax[A] { | |
| def self: A = a | |
| def F: Monoid[A] = implicitly[Monoid[A]] | |
| def zero = F.zero | |
| } | |
| 3 |+| 4 // 7 | |
| implicit def optionInstances[A: Monoid] = new Monoid[Option[A]] { | |
| def append(a: Option[A], b: Option[A]): Option[A] = { | |
| (a, b) match { | |
| case (Some(a1), Some(b1)) => Some(Monoid[A].append(a1, b1)) | |
| case (Some(_), None) => a | |
| case (None, Some(_)) => b | |
| case _ => None | |
| } | |
| } | |
| def zero = None | |
| } | |
| Option(30).append(Option(40)) == (Option(30) |+| Option(40)) // true; both equals to Option(70) | |
| implicit def mapInstances[K, V: Monoid] = new Monoid[Map[K, V]] { | |
| def append(a: Map[K, V], b: Map[K, V]): Map[K, V] = { | |
| (a, b) match { | |
| case (a: Map[K, V], b: Map[K, V]) => a.foldLeft(b) { | |
| case (m, v) => | |
| m + (v._1 -> Monoid[V].append(m.getOrElse(v._1, Monoid[V].zero), v._2)) | |
| } | |
| } | |
| } | |
| def zero = Map.empty[K, V] | |
| } | |
| implicit def mapOptionInstances[K, V: Monoid[Option[_]]] = new Monoid[Map[K, Option[V]]] { | |
| def append(a: Map[K, Option[V]], b: Map[K, Option[V]]): Map[K, Option[V]] = { | |
| (a, b) match { | |
| case (a: Map[K, Option[V]], b: Map[K, Option[V]]) => a.foldLeft(b) { | |
| case (m, v) => | |
| m + (v._1 -> Monoid[Option[V]].append(m.getOrElse(v._1, Monoid[Option[V]].zero), v._2)) | |
| } | |
| } | |
| } | |
| def zero = Map.empty[K, Option[V]] | |
| } | |
| val m = Map[Symbol, Int]() | |
| val n = Map('a -> 1, 'b -> 2) | |
| val x = m |+| n | |
| val a = Map('a -> 2, 'b -> 3, 'c -> 7) | |
| val b = Map('a -> 1, 'b -> 2) | |
| val answer = Map('a -> 3, 'b -> 5, 'c -> 7) | |
| (a |+| b) == answer | |
| val aOpt = Map('a -> Some(2), 'b -> Some(3), 'c -> Some(7)) | |
| val bOpt = Map('a -> Some(1), 'b -> Some(2)) | |
| val ansOpt = Map('a -> Some(3), 'b -> Some(5), 'c -> Some(7)) | |
| (aOpt |+| bOpt) == ansOpt |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment