-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Abhijit Sarkar
committed
Jan 12, 2024
1 parent
df79291
commit 5e3a9b8
Showing
17 changed files
with
351 additions
and
8 deletions.
There are no files selected for viewing
This file contains 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
2 changes: 1 addition & 1 deletion
2
src/main/scala/intro/eq/Cat.scala → src/main/scala/ch01/eq/Cat.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package intro.eq | ||
package ch01.eq | ||
|
||
import cats.Eq | ||
import cats.syntax.eq.catsSyntaxEq | ||
|
2 changes: 1 addition & 1 deletion
2
src/main/scala/intro/printable/Cat.scala → src/main/scala/ch01/printable/Cat.scala
This file contains 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
2 changes: 1 addition & 1 deletion
2
...ain/scala/intro/printable/Printable.scala → ...main/scala/ch01/printable/Printable.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package intro.printable | ||
package ch01.printable | ||
|
||
/* | ||
1.3 Exercise: Printable Library. | ||
|
2 changes: 1 addition & 1 deletion
2
src/main/scala/intro/show/Cat.scala → src/main/scala/ch01/show/Cat.scala
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package intro.show | ||
package ch01.show | ||
|
||
import cats.Show | ||
import cats.instances.int.catsStdShowForInt | ||
|
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
package ch02 | ||
import cats.{Monoid as CatsMonoid} | ||
import cats.syntax.semigroup.catsSyntaxSemigroup | ||
|
||
object Lib: | ||
|
||
/* | ||
2.5.4 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](items: List[A]): A = | ||
items.foldLeft(CatsMonoid[A].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] with | ||
def combine(o1: Order, o2: Order) = | ||
Order( | ||
o1.totalCost + o2.totalCost, | ||
o1.quantity + o2.quantity | ||
) | ||
|
||
def empty = Order(0, 0) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package ch02 | ||
|
||
trait Monoid[A] extends Semigroup[A]: | ||
def empty: A | ||
|
||
object Monoid: | ||
def apply[A](using monoid: Monoid[A]): Monoid[A] = | ||
monoid | ||
|
||
object MonoidInstances: | ||
/* | ||
2.3 Exercise: The Truth About Monoids | ||
Consider Boolean. How many monoids can you define for this type? | ||
*/ | ||
given booleanAndMonoid: Monoid[Boolean] with | ||
def combine(a: Boolean, b: Boolean) = a && b | ||
def empty = true | ||
|
||
given booleanOrMonoid: Monoid[Boolean] with | ||
def combine(a: Boolean, b: Boolean) = a || b | ||
def empty = false | ||
|
||
given booleanXorMonoid: Monoid[Boolean] with | ||
def combine(a: Boolean, b: Boolean) = | ||
a != b | ||
def empty = false | ||
|
||
// The negation of XOR | ||
given booleanXnorMonoid: Monoid[Boolean] with | ||
def combine(a: Boolean, b: Boolean) = | ||
(!a || b) && (a || !b) | ||
|
||
def empty = true | ||
|
||
/* | ||
2.4 Exercise: All Set for Monoids | ||
What monoids and semigroups are there for sets? | ||
*/ | ||
given setUnionMonoid[A]: Monoid[Set[A]] with | ||
def combine(a: Set[A], b: Set[A]) = a union b | ||
def empty = Set.empty[A] | ||
|
||
given symDiffMonoid[A]: Monoid[Set[A]] with | ||
def combine(a: Set[A], b: Set[A]): Set[A] = | ||
(a diff b) union (b diff a) | ||
def empty: Set[A] = Set.empty | ||
|
||
// given intAdditionMonoid: Monoid[Int] with | ||
// def combine(x: Int, y: Int): Int = x + y | ||
// def empty: Int = 0 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package ch02 | ||
|
||
trait Semigroup[A]: | ||
def combine(x: A, y: A): A | ||
|
||
object SemigroupInstances: | ||
// No identity element, can't form a Monoid. | ||
given setIntersectionSemigroup[A]: Semigroup[Set[A]] with | ||
def combine(a: Set[A], b: Set[A]) = | ||
a intersect b |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package ch03 | ||
|
||
/* | ||
3.5.6.1 Transformative Thinking with imap | ||
Implement the imap method for Codec. | ||
*/ | ||
trait Codec[A]: | ||
def encode(value: A): String | ||
def decode(value: String): A | ||
def imap[B](dec: A => B, enc: B => A): Codec[B] = | ||
val self = this | ||
new Codec[B]: | ||
override def encode(value: B): String = | ||
self.encode(enc(value)) | ||
|
||
override def decode(value: String): B = | ||
dec(self.decode(value)) | ||
|
||
def encode[A](value: A)(using c: Codec[A]): String = | ||
c.encode(value) | ||
|
||
def decode[A](value: String)(using c: Codec[A]): A = | ||
c.decode(value) | ||
|
||
object Codec: | ||
final case class Box[A](value: A) | ||
|
||
object CodecInstances: | ||
given stringCodec: Codec[String] with | ||
def encode(value: String): String = value | ||
def decode(value: String): String = value | ||
|
||
given Codec[Int] = | ||
stringCodec.imap(_.toInt, _.toString) | ||
|
||
// Demonstrate your imap method works by creating a Codec for Double. | ||
given Codec[Double] = | ||
stringCodec.imap(_.toDouble, _.toString) | ||
|
||
// Implement a Codec for the Box type. | ||
given boxCodec[A](using c: Codec[A]): Codec[Codec.Box[A]] = | ||
c.imap[Codec.Box[A]](Codec.Box(_), _.value) | ||
|
||
import CodecInstances.given | ||
|
||
val ca = encode(123.4) | ||
val cb = decode[Double]("123.4") | ||
|
||
val cc = encode(Codec.Box(123.4)) | ||
val cd = decode[Codec.Box[Double]]("123.4") |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package ch03 | ||
|
||
/* | ||
3.5.5.1 Exercise: Showing off with Contramap | ||
Implement the contramap method for Printable above. | ||
*/ | ||
trait Printable[A]: | ||
def format(value: A): String | ||
|
||
def contramap[B](f: B => A): Printable[B] = | ||
val self = this | ||
new Printable[B]: | ||
def format(value: B): String = | ||
self.format(f(value)) | ||
|
||
def format[A](value: A)(using p: Printable[A]): String = | ||
p.format(value) | ||
|
||
object Printable: | ||
final case class Box[A](value: A) | ||
|
||
object PrintableInstances: | ||
given Printable[String] with | ||
def format(value: String): String = | ||
s"'${value}'" | ||
|
||
given Printable[Boolean] with | ||
def format(value: Boolean): String = | ||
if (value) "yes" else "no" | ||
|
||
/* | ||
Define an instance of Printable for the following Box case class. | ||
Rather than writing out the complete definition from scratch | ||
(new Printable[Box] etc...), create your instance from an existing | ||
instance using contramap. | ||
*/ | ||
// If we use `with`, method format has to be defined. | ||
// Given a Printable[A], and a Box[A], we can create | ||
// Printable[Box[A]] by the reverse mapping Box[A] => A. | ||
// The forward mapping would be A => Box[A]. | ||
given [A](using p: Printable[A]): Printable[Printable.Box[A]] = | ||
p.contramap[Printable.Box[A]](_.value) | ||
|
||
import PrintableInstances.given | ||
|
||
val ps = format("hello") | ||
val pb = format(true) | ||
val pc = format(Printable.Box("hello")) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ch03 | ||
|
||
import cats.Functor | ||
|
||
sealed trait Tree[+A] | ||
|
||
final case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A] | ||
|
||
final case class Leaf[A](value: A) extends Tree[A] | ||
|
||
object Tree: | ||
given Functor[Tree] with | ||
override def map[A, B](fa: Tree[A])(f: A => B): Tree[B] = | ||
fa match | ||
case Branch(l, r) => Branch(map(l)(f), map(r)(f)) | ||
case Leaf(value) => Leaf(f(value)) | ||
|
||
def branch[A](left: Tree[A], right: Tree[A]): Tree[A] = | ||
Branch(left, right) | ||
|
||
def leaf[A](value: A): Tree[A] = | ||
Leaf(value) |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import cats.Functor | ||
import cats.syntax.functor.toFunctorOps | ||
|
||
// 3.5.2 Functor Syntax | ||
val func1 = (a: Int) => a + 1 | ||
val func2 = (a: Int) => a * 2 | ||
val func3 = (a: Int) => s"${a}!" | ||
val func4 = func1.map(func2).map(func3) | ||
|
||
func4(123) | ||
|
||
def doMath[F[_]](start: F[Int])(using functor: Functor[F]): F[Int] = | ||
start.map(n => n + 1 * 2) | ||
|
||
doMath(Option(20)) | ||
doMath(List(1, 2, 3)) | ||
|
||
// 3.6.1 Contravariant in Cats | ||
import cats.Contravariant | ||
import cats.Show | ||
import cats.instances.string.catsStdShowForString | ||
|
||
val showString = Show[String] | ||
|
||
// trait Contravariant[F[_]] { | ||
// def contramap[A, B](fa: F[A])(f: B => A): F[B] | ||
// } | ||
|
||
val showSymbol: Show[Symbol] = Contravariant[Show]. | ||
contramap(showString)((sym: Symbol) => s"'${sym.name}") | ||
|
||
showSymbol.show(Symbol("dave")) | ||
|
||
import cats.syntax.contravariant.toContravariantOps | ||
|
||
showString | ||
.contramap[Symbol](sym => s"'${sym.name}") | ||
.show(Symbol("dave")) | ||
|
||
|
||
// 3.6.2 Invariant in Cats | ||
import cats.Monoid | ||
import cats.instances.string.catsKernelStdMonoidForString | ||
import cats.syntax.invariant.toInvariantOps | ||
import cats.syntax.semigroup.catsSyntaxSemigroup | ||
|
||
given Monoid[Symbol] = | ||
Monoid[String].imap(Symbol.apply)(_.name) | ||
|
||
Monoid[Symbol].empty | ||
|
||
Symbol("a") |+| Symbol("few") |+| Symbol("words") | ||
|
||
// 3.7 Aside: Partial Unification | ||
val f1 = (x: Int) => x.toDouble | ||
val f2 = (y: Double) => y * 2 | ||
|
||
val f3 = func1.map(func2) | ||
|
||
val either: Either[String, Int] = Right(123) | ||
|
||
either.map(_ + 1) | ||
|
||
val f3a: Int => Double = | ||
a => f2(f1(a)) | ||
|
||
val f3b: Int => Double = | ||
f2.compose(f1) | ||
|
||
// error: value contramap is not a member of Double => Double | ||
// val f3c = f2.contramap(f1) | ||
|
||
type <=[B, A] = A => B | ||
|
||
type F[A] = Double <= A | ||
|
||
val f2b: Double <= Double = f2 | ||
|
||
val f3c = f2b.contramap(f1) |
2 changes: 1 addition & 1 deletion
2
src/test/scala/intro/eq/CatSpec.scala → src/test/scala/ch01/eq/CatSpec.scala
This file contains 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
2 changes: 1 addition & 1 deletion
2
src/test/scala/intro/printable/CatSpec.scala → src/test/scala/ch01/printable/CatSpec.scala
This file contains 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
2 changes: 1 addition & 1 deletion
2
src/test/scala/intro/show/CatSpec.scala → src/test/scala/ch01/show/CatSpec.scala
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package ch02 | ||
import org.scalatest.funspec.AnyFunSpec | ||
import org.scalatest.matchers.should.Matchers.shouldBe | ||
import ch02.Lib.add | ||
|
||
class MonoidSpec extends AnyFunSpec: | ||
describe("Monoid"): | ||
it("should be able to add integers"): | ||
val ints = List(1, 2, 3) | ||
add(ints) `shouldBe` 6 | ||
|
||
it("should be able to add strings"): | ||
val strings = List("Hi ", "there") | ||
add(strings) `shouldBe` "Hi there" | ||
|
||
it("should be able to add sets"): | ||
val sets = List(Set("A", "B"), Set("B", "C")) | ||
add(sets) `shouldBe` Set("A", "B", "C") | ||
|
||
it("should be able to add options"): | ||
val opts = List(Option(22), Option(20)) | ||
add(opts) `shouldBe` Option(42) |
Oops, something went wrong.