Skip to content

Commit 045f097

Browse files
author
Abhijit Sarkar
committed
Complete ch07
1 parent ce1f067 commit 045f097

File tree

6 files changed

+130
-1
lines changed

6 files changed

+130
-1
lines changed

.scalafmt.conf

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ assumeStandardLibraryStripMargin = true
66
# https://github.com/scalameta/scalameta/issues/4090
77
project.excludePaths = [
88
"glob:**/ch04/src/**.scala",
9-
"glob:**/ch06/src/Cat.scala"
9+
"glob:**/ch06/src/Cat.scala",
10+
"glob:**/ch07/src/*.scala"
1011
]

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ The older code is available in branches.
1414
4. [Contextual Abstraction](ch04)
1515
5. [Reified Interpreters](ch05)
1616
6. [Using Cats](ch06)
17+
7. [Monoids and Semigroups](ch07)
1718

1819
## Running tests
1920
```

ch07/src/Lib.scala

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package ch07
2+
3+
/*
4+
We can use Semigroups and Monoids by importing two things: the type classes themselves,
5+
and the semigroup syntax to give us the |+| operator.
6+
*/
7+
import cats.{Monoid as CatsMonoid}
8+
import cats.syntax.semigroup.catsSyntaxSemigroup
9+
10+
object Lib:
11+
12+
/*
13+
7.3.3.1 Exercise: Adding All The Things
14+
The cutting edge SuperAdder v3.5a-32 is the world's first choice for adding together numbers.
15+
The main function in the program has signature def add(items: List[Int]): Int.
16+
In a tragic accident this code is deleted! Rewrite the method and save the day!
17+
18+
SuperAdder's market share continues to grow, and now there is demand for additional functionality.
19+
People now want to add List[Option[Int]]. Change add so this is possible. The SuperAdder code base
20+
is of the highest quality, so make sure there is no code duplication!
21+
*/
22+
def add[A: CatsMonoid as m](items: List[A]): A =
23+
items.foldLeft(m.empty)(_ |+| _)
24+
25+
// import cats.instances.int.catsKernelStdGroupForInt
26+
// add(List(1, 2, 3))
27+
28+
// add(List(Some(1), None, Some(2), None, Some(3)))
29+
30+
// Doesn't compile: No given instance of type cats.kernel.Monoid[Some[Int]] was found.
31+
// The inferred type of the list is List[Some[Int]], Cats Monoid is invariant, so,
32+
// Monoid[Option[A]] is not applicable.
33+
// add(List(Some(1), Some(2), Some(3)))
34+
35+
/*
36+
SuperAdder is entering the POS (point-of-sale, not the other POS) market.
37+
Now we want to add up Orders.
38+
39+
We need to release this code really soon so we can’t make any modifications to add.
40+
Make it so!
41+
*/
42+
case class Order(totalCost: Double, quantity: Double)
43+
44+
given Monoid[Order]:
45+
def combine(o1: Order, o2: Order) =
46+
Order(
47+
o1.totalCost + o2.totalCost,
48+
o1.quantity + o2.quantity
49+
)
50+
51+
def empty = Order(0, 0)

ch07/src/Monoid.scala

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package ch07
2+
3+
trait Monoid[A] extends Semigroup[A]:
4+
def empty: A
5+
6+
object Monoid:
7+
def apply[A : Monoid as m]: Monoid[A] = m
8+
9+
/*
10+
7.2.0.1 Exercise: The Truth About Monoids
11+
Consider Boolean. How many monoids can you define for this type?
12+
*/
13+
given booleanAndMonoid: Monoid[Boolean]:
14+
def combine(a: Boolean, b: Boolean) = a && b
15+
def empty = true
16+
17+
given booleanOrMonoid: Monoid[Boolean]:
18+
def combine(a: Boolean, b: Boolean) = a || b
19+
def empty = false
20+
21+
given booleanXorMonoid: Monoid[Boolean]:
22+
def combine(a: Boolean, b: Boolean) =
23+
a != b
24+
def empty = false
25+
26+
// The negation of XOR
27+
given booleanXnorMonoid: Monoid[Boolean]:
28+
def combine(a: Boolean, b: Boolean) =
29+
(!a || b) && (a || !b)
30+
31+
def empty = true
32+
33+
/*
34+
7.2.0.2 Exercise: All Set for Monoids
35+
What monoids and semigroups are there for sets?
36+
*/
37+
given setUnionMonoid: [A] => Monoid[Set[A]]:
38+
def combine(a: Set[A], b: Set[A]) = a.union(b)
39+
def empty = Set.empty[A]
40+
41+
given symDiffMonoid: [A] => Monoid[Set[A]]:
42+
def combine(a: Set[A], b: Set[A]): Set[A] =
43+
(a.diff(b)).union(b.diff(a))
44+
def empty: Set[A] = Set.empty

ch07/src/Semigroup.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package ch07
2+
3+
trait Semigroup[A]:
4+
def combine(x: A, y: A): A
5+
6+
object Semigroup:
7+
// No identity element, can't form a Monoid.
8+
given setIntersectionSemigroup: [A] => Semigroup[Set[A]]:
9+
def combine(a: Set[A], b: Set[A]) =
10+
a.intersect(b)

ch07/test/src/LibSpec.scala

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package ch07
2+
import org.scalatest.funspec.AnyFunSpec
3+
import org.scalatest.matchers.should.Matchers.shouldBe
4+
import ch07.Lib.add
5+
6+
class LibSpec extends AnyFunSpec:
7+
describe("Monoid"):
8+
it("should add integers"):
9+
val ints = List(1, 2, 3)
10+
add(ints) `shouldBe` 6
11+
12+
it("should add strings"):
13+
val strings = List("Hi ", "there")
14+
add(strings) `shouldBe` "Hi there"
15+
16+
it("should add sets"):
17+
val sets = List(Set("A", "B"), Set("B", "C"))
18+
add(sets) `shouldBe` Set("A", "B", "C")
19+
20+
it("should add options"):
21+
val opts = List(Option(22), Option(20))
22+
add(opts) `shouldBe` Option(42)

0 commit comments

Comments
 (0)