Skip to content

Commit

Permalink
Complete ch07
Browse files Browse the repository at this point in the history
  • Loading branch information
Abhijit Sarkar committed Dec 22, 2024
1 parent ce1f067 commit 045f097
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ assumeStandardLibraryStripMargin = true
# https://github.com/scalameta/scalameta/issues/4090
project.excludePaths = [
"glob:**/ch04/src/**.scala",
"glob:**/ch06/src/Cat.scala"
"glob:**/ch06/src/Cat.scala",
"glob:**/ch07/src/*.scala"
]
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The older code is available in branches.
4. [Contextual Abstraction](ch04)
5. [Reified Interpreters](ch05)
6. [Using Cats](ch06)
7. [Monoids and Semigroups](ch07)

## Running tests
```
Expand Down
51 changes: 51 additions & 0 deletions ch07/src/Lib.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ch07

/*
We can use Semigroups and Monoids by importing two things: the type classes themselves,
and the semigroup syntax to give us the |+| operator.
*/
import cats.{Monoid as CatsMonoid}
import cats.syntax.semigroup.catsSyntaxSemigroup

object Lib:

/*
7.3.3.1 Exercise: Adding All The Things
The cutting edge SuperAdder v3.5a-32 is the world's first choice for adding together numbers.
The main function in the program has signature def add(items: List[Int]): Int.
In a tragic accident this code is deleted! Rewrite the method and save the day!
SuperAdder's market share continues to grow, and now there is demand for additional functionality.
People now want to add List[Option[Int]]. Change add so this is possible. The SuperAdder code base
is of the highest quality, so make sure there is no code duplication!
*/
def add[A: CatsMonoid as m](items: List[A]): A =
items.foldLeft(m.empty)(_ |+| _)

// import cats.instances.int.catsKernelStdGroupForInt
// add(List(1, 2, 3))

// add(List(Some(1), None, Some(2), None, Some(3)))

// Doesn't compile: No given instance of type cats.kernel.Monoid[Some[Int]] was found.
// The inferred type of the list is List[Some[Int]], Cats Monoid is invariant, so,
// Monoid[Option[A]] is not applicable.
// add(List(Some(1), Some(2), Some(3)))

/*
SuperAdder is entering the POS (point-of-sale, not the other POS) market.
Now we want to add up Orders.
We need to release this code really soon so we can’t make any modifications to add.
Make it so!
*/
case class Order(totalCost: Double, quantity: Double)

given Monoid[Order]:
def combine(o1: Order, o2: Order) =
Order(
o1.totalCost + o2.totalCost,
o1.quantity + o2.quantity
)

def empty = Order(0, 0)
44 changes: 44 additions & 0 deletions ch07/src/Monoid.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ch07

trait Monoid[A] extends Semigroup[A]:
def empty: A

object Monoid:
def apply[A : Monoid as m]: Monoid[A] = m

/*
7.2.0.1 Exercise: The Truth About Monoids
Consider Boolean. How many monoids can you define for this type?
*/
given booleanAndMonoid: Monoid[Boolean]:
def combine(a: Boolean, b: Boolean) = a && b
def empty = true

given booleanOrMonoid: Monoid[Boolean]:
def combine(a: Boolean, b: Boolean) = a || b
def empty = false

given booleanXorMonoid: Monoid[Boolean]:
def combine(a: Boolean, b: Boolean) =
a != b
def empty = false

// The negation of XOR
given booleanXnorMonoid: Monoid[Boolean]:
def combine(a: Boolean, b: Boolean) =
(!a || b) && (a || !b)

def empty = true

/*
7.2.0.2 Exercise: All Set for Monoids
What monoids and semigroups are there for sets?
*/
given setUnionMonoid: [A] => Monoid[Set[A]]:
def combine(a: Set[A], b: Set[A]) = a.union(b)
def empty = Set.empty[A]

given symDiffMonoid: [A] => Monoid[Set[A]]:
def combine(a: Set[A], b: Set[A]): Set[A] =
(a.diff(b)).union(b.diff(a))
def empty: Set[A] = Set.empty
10 changes: 10 additions & 0 deletions ch07/src/Semigroup.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ch07

trait Semigroup[A]:
def combine(x: A, y: A): A

object Semigroup:
// No identity element, can't form a Monoid.
given setIntersectionSemigroup: [A] => Semigroup[Set[A]]:
def combine(a: Set[A], b: Set[A]) =
a.intersect(b)
22 changes: 22 additions & 0 deletions ch07/test/src/LibSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package ch07
import org.scalatest.funspec.AnyFunSpec
import org.scalatest.matchers.should.Matchers.shouldBe
import ch07.Lib.add

class LibSpec extends AnyFunSpec:
describe("Monoid"):
it("should add integers"):
val ints = List(1, 2, 3)
add(ints) `shouldBe` 6

it("should add strings"):
val strings = List("Hi ", "there")
add(strings) `shouldBe` "Hi there"

it("should add sets"):
val sets = List(Set("A", "B"), Set("B", "C"))
add(sets) `shouldBe` Set("A", "B", "C")

it("should add options"):
val opts = List(Option(22), Option(20))
add(opts) `shouldBe` Option(42)

0 comments on commit 045f097

Please sign in to comment.