From 47fe21019ad55497188b54ee87f0fbe0aa2a64d9 Mon Sep 17 00:00:00 2001 From: Kevin Lee Date: Sat, 28 Sep 2019 15:44:21 +1000 Subject: [PATCH] Close #82 - Add OptionSemiGroup and OptionMonoid --- src/main/scala/just/fp/Monoid.scala | 14 ++++++++-- src/main/scala/just/fp/SemiGroup.scala | 26 ++++++++++++++++++- src/test/scala/just/fp/MonoidSpec.scala | 12 ++++++++- .../just/fp/syntax/SemiGroupSyntaxSpec.scala | 4 ++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/main/scala/just/fp/Monoid.scala b/src/main/scala/just/fp/Monoid.scala index c20b2cf..aca2ad5 100644 --- a/src/main/scala/just/fp/Monoid.scala +++ b/src/main/scala/just/fp/Monoid.scala @@ -33,7 +33,7 @@ trait Monoid[A] extends SemiGroup[A] { def monoidLaw: MonoidLaw = new MonoidLaw {} } -object Monoid { +object Monoid extends OptionMonoidInstance { implicit def listMonoid[A]: Monoid[List[A]] = new Monoid[List[A]] with ListSemiGroup[A] { override def zero: List[A] = Nil @@ -75,4 +75,14 @@ object Monoid { override def zero: BigDecimal = BigDecimal(0) } -} \ No newline at end of file +} + +private[fp] trait OptionMonoidInstance extends OptionSemiGroupInstance { + implicit def optionMonoid[A](implicit F0: SemiGroup[A]): Monoid[Option[A]] = + new Monoid[Option[A]] with OptionSemigroup[A] { + + override implicit def F: SemiGroup[A] = F0 + + override def zero: Option[A] = None + } +} diff --git a/src/main/scala/just/fp/SemiGroup.scala b/src/main/scala/just/fp/SemiGroup.scala index 4e194c7..bf2d842 100644 --- a/src/main/scala/just/fp/SemiGroup.scala +++ b/src/main/scala/just/fp/SemiGroup.scala @@ -8,7 +8,7 @@ trait SemiGroup[A] { def append(a1: A, a2: => A): A } -object SemiGroup { +object SemiGroup extends OptionSemiGroupInstance { trait ListSemiGroup[A] extends SemiGroup[List[A]] { override def append(a1: List[A], a2: => List[A]): List[A] = a1 ++ a2 @@ -60,3 +60,27 @@ object SemiGroup { } implicit val bigDecimalSemiGroup: SemiGroup[BigDecimal] = new BigDecimalSemiGroup {} } + +private[fp] trait OptionSemigroup[A] extends SemiGroup[Option[A]] { + + implicit def F: SemiGroup[A] + + override def append(a1: Option[A], a2: => Option[A]): Option[A] = (a1, a2) match { + case (Some(x), Some(y)) => + Some(implicitly[SemiGroup[A]].append(x, y)) + case (Some(x), None) => + Some(x) + case (None, Some(y)) => + Some(y) + case (None, None) => + None + } +} + +private[fp] trait OptionSemiGroupInstance { + + implicit def optionSemigroup[A](implicit F0: SemiGroup[A]): SemiGroup[Option[A]] = + new OptionSemigroup[A] { + override implicit def F: SemiGroup[A] = F0 + } +} \ No newline at end of file diff --git a/src/test/scala/just/fp/MonoidSpec.scala b/src/test/scala/just/fp/MonoidSpec.scala index fb66509..f9c59ba 100644 --- a/src/test/scala/just/fp/MonoidSpec.scala +++ b/src/test/scala/just/fp/MonoidSpec.scala @@ -10,7 +10,8 @@ import hedgehog.runner._ object MonoidSpec extends Properties { override def tests: List[Test] = List( - property("testListMonoidLaw", ListMonoidLaws.laws) + property("testOptionMonoidLaw", OptionMonoidLaws.laws) + , property("testListMonoidLaw", ListMonoidLaws.laws) , property("testVectorMonoidLaw", VectorMonoidLaws.laws) , property("testStringMonoidLaw", StringMonoidLaws.laws) , property("testByteMonoidLaw", ByteMonoidLaws.laws) @@ -40,6 +41,15 @@ object MonoidSpec extends Properties { ) } + object OptionMonoidLaws { + def genOption: Gen[Option[Int]] = Gens.genOption(Gens.genIntFromMinToMax) + + def laws: Property = + Specs.monoidLaws.laws[Option[Int]]( + genOption + ) + } + object StringMonoidLaws { def genString: Gen[String] = Gens.genUnicodeString diff --git a/src/test/scala/just/fp/syntax/SemiGroupSyntaxSpec.scala b/src/test/scala/just/fp/syntax/SemiGroupSyntaxSpec.scala index dd5fad9..3ddb8cc 100644 --- a/src/test/scala/just/fp/syntax/SemiGroupSyntaxSpec.scala +++ b/src/test/scala/just/fp/syntax/SemiGroupSyntaxSpec.scala @@ -12,7 +12,8 @@ object SemiGroupSyntaxSpec extends Properties { import just.fp._ override def tests: List[Test] = List( - property("test List |+| List", testPlus(Gens.genList(Gens.genIntFromMinToMax, 10))) + property("test Option |+| Option", testPlus(Gens.genOption(Gens.genIntFromMinToMax))) + , property("test List |+| List", testPlus(Gens.genList(Gens.genIntFromMinToMax, 10))) , property("test Vector |+| Vector", testPlus(Gens.genVector(Gens.genIntFromMinToMax, 10))) , property("test String |+| String", testPlus(Gens.genUnicodeString)) , property("test Byte |+| Byte", testPlus(Gens.genByteFromMinToMax)) @@ -22,6 +23,7 @@ object SemiGroupSyntaxSpec extends Properties { , property("test Long |+| Long", testPlus(Gens.genLongFromMinToMax)) , property("test BigInt |+| BigInt", testPlus(Gens.genBigInt)) , property("test BigDecimal |+| BigDecimal", testPlus(Gens.genBigDecimal)) + , property("test Option.mappend(Option)", testMappend(Gens.genOption(Gens.genIntFromMinToMax))) , property("test List.mappend(List)", testMappend(Gens.genList(Gens.genIntFromMinToMax, 10))) , property("test Vector.mappend(Vector)", testMappend(Gens.genVector(Gens.genIntFromMinToMax, 10))) , property("test String.mappend(String)", testMappend(Gens.genUnicodeString))