Practical examples for Scala Cats in context of shopping cart
My experience trying to learn Functional Programming was painful. Reading blogs after blogs makes it more and more confusing. So I took the practical approach to learn and this is my effort to create a reference and make it easier for anyone who passes by my blog. As shopping cart is a common appl, I have used it as the basis for these examples
- in FP we try to decouple data from behaviour
- model your domain using ADT’s(Algebraic Data Types)
- Sum => choice or union using sealed abstract classes or trait
eg:
sealed abstract class CartStatus case object InProgress extends CartStatus case object Purchased extends CartStatus case object Dropped extends CartStatus
- Product type => case class
eg:composition saying we have a and b and c
case class Checkout( item: Items, cartNumber: CartNumber)
- Sum => choice or union using sealed abstract classes or trait
eg:
- Use type system to your advantage
// anyval means value class. At runtime `AnyVal` gets removed only used during compile time for compile error
Option
import cats.syntax.option._
def someHttpCall(): Future {
return futureResult
}
// the value is upcast to Option[T] from Some[T]
Cat 1: Functor
What: Functor is simply something that can be mapped over. In Scala map
there is no concrete implementation of functor. But there is map
which behaves like a functor.
When: When you need to map over a Option, List
Eg:
def getItemQuantity(item: Item, cart: Cart) {
return cart[item].quantity
}
Functor[List].map(List("iphone9", "samsung"))(_.getItemQuantity)
Cat 2: Monoid
What: Monoid is simply a trait/class with combine
and empty
functionality that follows associative laws.
trait Monoid[A] {
def combine(a: A, b: B): A
def empty: A
}
Eg: Say you want to find the total number of items that makeup the shopping cart
def totalItems(items: List[Int]): Int = items.foldLeft(Monoid[Int].empty)(_ |+| _)
Cat 3: Type-safe Equality(Eq)
What: Cats support both ===
and =!=
for equality and non-equality respectively
Eg: ```// custom equality
import cats.instances.long._
implicit val productEq: Eq(Product) =
Eq.instance[Product] {(product1, product2) =>
product1.name === product2.name
product1.sku === product2.sku
}