From e331d2a71beb4814ab1eb191e7dc9cf4078d9f00 Mon Sep 17 00:00:00 2001 From: Teo Danciu Date: Fri, 8 Mar 2019 17:23:55 +0000 Subject: [PATCH 1/3] Introduce `Monoids` empty section --- .../scala/fpinscalalib/FPinScalaLibrary.scala | 3 ++- .../scala/fpinscalalib/MonoidsSection.scala | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/fpinscalalib/MonoidsSection.scala diff --git a/src/main/scala/fpinscalalib/FPinScalaLibrary.scala b/src/main/scala/fpinscalalib/FPinScalaLibrary.scala index 13127cf..c1b58c9 100644 --- a/src/main/scala/fpinscalalib/FPinScalaLibrary.scala +++ b/src/main/scala/fpinscalalib/FPinScalaLibrary.scala @@ -25,7 +25,8 @@ object FPinScalaLibrary extends Library { FunctionalStateSection, FunctionalParallelismSection, PropertyBasedTestingSection, - ParserCombinatorsSection + ParserCombinatorsSection, + MonoidsSection ) override def logoPath = "fp_in_scala" diff --git a/src/main/scala/fpinscalalib/MonoidsSection.scala b/src/main/scala/fpinscalalib/MonoidsSection.scala new file mode 100644 index 0000000..b169850 --- /dev/null +++ b/src/main/scala/fpinscalalib/MonoidsSection.scala @@ -0,0 +1,21 @@ +package fpinscalalib + +import org.scalatest.Matchers +import org.scalatest.FlatSpec + +object MonoidsSection extends FlatSpec + with Matchers + with org.scalaexercises.definitions.Section { + + /** + * = Functional programming in Scala = + * + * The following set of sections represent the exercises contained in the book "Functional Programming in Scala", + * written by Paul Chiusano and RĂșnar Bjarnason and published by Manning. This content library is meant to be used + * in tandem with the book. We use the same numeration for the exercises for you to follow them. + * + * For more information about "Functional Programming in Scala" please visit its + * official website. + * + **/ +} From 94c27aaa0a47ffa265771306e121ee70839454b4 Mon Sep 17 00:00:00 2001 From: Teo Danciu Date: Fri, 8 Mar 2019 17:28:46 +0000 Subject: [PATCH 2/3] Add exercises: 10.1, 10.2 and associated tests --- .../scala/fpinscalalib/MonoidsSection.scala | 74 +++++++++++++++++++ .../customlib/monoids/Monoid.scala | 6 ++ src/test/scala/fpinscalalib/MonoidsSpec.scala | 11 +++ 3 files changed, 91 insertions(+) create mode 100644 src/main/scala/fpinscalalib/customlib/monoids/Monoid.scala create mode 100644 src/test/scala/fpinscalalib/MonoidsSpec.scala diff --git a/src/main/scala/fpinscalalib/MonoidsSection.scala b/src/main/scala/fpinscalalib/MonoidsSection.scala index b169850..86533da 100644 --- a/src/main/scala/fpinscalalib/MonoidsSection.scala +++ b/src/main/scala/fpinscalalib/MonoidsSection.scala @@ -1,5 +1,6 @@ package fpinscalalib +import fpinscalalib.customlib.monoids.Monoid import org.scalatest.Matchers import org.scalatest.FlatSpec @@ -17,5 +18,78 @@ object MonoidsSection extends FlatSpec * For more information about "Functional Programming in Scala" please visit its * official website. * + * = What is a monoid? = + * + * Exercise 10.1 + * + * Give Monoid instances for integer addition and multiplication as well as the Boolean operators + * + * + * Let's implement Monoid instances for integer addition and multiplication as well as the Boolean operators, taking this representation of `Monoid`: + * + * {{{ + * trait Monoid[A] { + * def op(a1: A, a2: A): A + * def zero: A + * } + * }}} **/ + def monoidInstancesAssert( + res0: Int, + res1: Int, + res2: Boolean, + res3: Boolean + ): Unit = { + + val intAddition: Monoid[Int] = new Monoid[Int] { + def op(x: Int, y: Int): Int = x + y + def zero: Int = res0 + } + + val intMultipication: Monoid[Int] = new Monoid[Int] { + def op(x: Int, y: Int): Int = x * y + def zero: Int = res1 + } + + val booleanOr: Monoid[Boolean] = new Monoid[Boolean] { + def op(x: Boolean, y: Boolean): Boolean = x || y + def zero: Boolean = res2 + } + + def booleanAnd: Monoid[Boolean] = new Monoid[Boolean] { + def op(x: Boolean, y: Boolean): Boolean = x && y + def zero: Boolean = res3 + } + + intAddition.op(intAddition.zero, 5) shouldBe 5 + intAddition.op(5, intAddition.zero) shouldBe 5 + + intMultipication.op(intMultipication.zero, 5) shouldBe 5 + intMultipication.op(5, intMultipication.zero) shouldBe 5 + + booleanOr.op(booleanOr.zero, true) shouldBe true + booleanOr.op(true, booleanOr.zero) shouldBe true + booleanOr.op(booleanOr.zero, false) shouldBe false + booleanOr.op(false, booleanOr.zero) shouldBe false + + booleanAnd.op(booleanAnd.zero, true) shouldBe true + booleanAnd.op(true, booleanAnd.zero) shouldBe true + booleanAnd.op(booleanAnd.zero, false) shouldBe false + booleanAnd.op(false, booleanAnd.zero) shouldBe false + } + + /** + * Exercise 10.2 + * + * Let's give a Monoid instance for combining Option values + */ + def optionMonoidAssert(res0: Option[Int], res1: Option[Int]): Unit = { + def optionMonoid[A]: Monoid[Option[A]] = new Monoid[Option[A]] { + def op(x: Option[A], y: Option[A]): Option[A] = x.orElse(y) + def zero: Option[A] = None + } + + optionMonoid[Int].op(Option(2), Option(3)) shouldBe res0 + optionMonoid[Int].op(Option(2), optionMonoid[Int].zero) shouldBe res1 + } } diff --git a/src/main/scala/fpinscalalib/customlib/monoids/Monoid.scala b/src/main/scala/fpinscalalib/customlib/monoids/Monoid.scala new file mode 100644 index 0000000..03ceaf2 --- /dev/null +++ b/src/main/scala/fpinscalalib/customlib/monoids/Monoid.scala @@ -0,0 +1,6 @@ +package fpinscalalib.customlib.monoids + +trait Monoid[A] { + def op(a1: A, a2: A): A + def zero: A +} diff --git a/src/test/scala/fpinscalalib/MonoidsSpec.scala b/src/test/scala/fpinscalalib/MonoidsSpec.scala new file mode 100644 index 0000000..9b40663 --- /dev/null +++ b/src/test/scala/fpinscalalib/MonoidsSpec.scala @@ -0,0 +1,11 @@ +package fpinscalalib + +import org.scalatest.refspec.RefSpec +import org.scalatest.prop.Checkers + +class MonoidsSpec extends RefSpec with Checkers { + def `monoid instances asserts` = { + MonoidsSection.monoidInstancesAssert(0, 1, false, true) + MonoidsSection.optionMonoidAssert(Option(2), Option(2)) + } +} From bf143c336236bf7a868ef18f81ada87d15922687 Mon Sep 17 00:00:00 2001 From: Teo Danciu Date: Fri, 8 Mar 2019 17:32:09 +0000 Subject: [PATCH 3/3] Add exercise 10.3 --- .../scala/fpinscalalib/MonoidsSection.scala | 22 +++++++++++++++++++ src/test/scala/fpinscalalib/MonoidsSpec.scala | 1 + 2 files changed, 23 insertions(+) diff --git a/src/main/scala/fpinscalalib/MonoidsSection.scala b/src/main/scala/fpinscalalib/MonoidsSection.scala index 86533da..bcc8df3 100644 --- a/src/main/scala/fpinscalalib/MonoidsSection.scala +++ b/src/main/scala/fpinscalalib/MonoidsSection.scala @@ -92,4 +92,26 @@ object MonoidsSection extends FlatSpec optionMonoid[Int].op(Option(2), Option(3)) shouldBe res0 optionMonoid[Int].op(Option(2), optionMonoid[Int].zero) shouldBe res1 } + + /** + * Exercise 10.3 + * + * Let's write a monoid for endofunctions (= functions having the same argument and return type) + */ + def endoMonoidAssert( + res0: Int, + res1: Int, + res2: Int, + res3: Int + ): Unit = { + def endoMonoid[A]: Monoid[A => A] = new Monoid[A => A] { + def op(f: A => A, g: A => A): A => A = x => f(g(x)) + def zero: A => A = x => x + } + + endoMonoid[Int].op(_ + 1, _ * 2)(10) shouldBe res0 + endoMonoid[Int].op(_ * 2, _ + 1)(10) shouldBe res1 + endoMonoid[Int].op(_ + 1, endoMonoid[Int].zero)(10) shouldBe res2 + endoMonoid[Int].op(endoMonoid[Int].zero, endoMonoid[Int].zero)(10) shouldBe res3 + } } diff --git a/src/test/scala/fpinscalalib/MonoidsSpec.scala b/src/test/scala/fpinscalalib/MonoidsSpec.scala index 9b40663..8044076 100644 --- a/src/test/scala/fpinscalalib/MonoidsSpec.scala +++ b/src/test/scala/fpinscalalib/MonoidsSpec.scala @@ -7,5 +7,6 @@ class MonoidsSpec extends RefSpec with Checkers { def `monoid instances asserts` = { MonoidsSection.monoidInstancesAssert(0, 1, false, true) MonoidsSection.optionMonoidAssert(Option(2), Option(2)) + MonoidsSection.endoMonoidAssert(21, 22, 11, 10) } }