SlideShare a Scribd company logo
1 of 227
Download to read offline
Having a cake and eating it
too.
Introduction to Type classes (in Scala)
“The Story of
a Blackout”
In 4 acts
Act.1: “Loss & Grief”
503 Service Temporarily Unavailable
nginx
503 Service Temporarily Unavailable
nginx
The 5 Stages of Loss and Grief
The 5 Stages of Loss and Grief
1. Denial
The 5 Stages of Loss and Grief
1. Denial
2. Anger
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
4. Depression
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
4. Depression
5. Acceptance
Act.2: “The Mess”
auction
auctionify
auctionify.io
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
Order
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
● Orders can be:
○ General - contain list of Products
○ Complex - combined from two or more
other Orders
○ Cancelled - used to be fully fledged
Orders, but now are cancelled
Order
General Complex Cancelled
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
● Orders can be:
○ General - contain list of Products
○ Complex - combined from two or more
other Orders
○ Cancelled - used to be fully fledged
Orders, but now are cancelled
● Products can be:
○ Basic - id & price
○ Discounted - wrapped Product &
discount
○ OutOfStock - used to be in warehouse, but
now gone (sorry)
Order
General Complex Cancelled
Product
Basic Discounter
Out of
Stock
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[error] OrderTest.scala evaluate is not a member of jw.ComplexOrder
[error] order.evaluate should equal (BigDecimal("11.0"))
[error] ^
[error] one error found
Subtype Polymorphism
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[error] OrderTest.scala evaluate is not a member of jw.ComplexOrder
[error] order.evaluate should equal (BigDecimal("11.0"))
[error] ^
[error] one error found
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[info] OrderTest:
[info] - should evaluate order
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
[error] OrderTest.scala average is not a member of jw.Order
[error] Order.average should equal (BigDecimal("5.0"))
[error] ^
[error] one error found
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal = ???
}
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal =
xs.reduce(_ + _) / xs.size
}
AWESOME!
I just need it to work for
Doubles and Ints as
well! Can you make it
more generic? Thx!
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal =
xs.reduce(_ + _) / xs.size
}
object Stat {
def mean(xs: Seq[Number]): Number =
xs.reduce(_ + _) / xs.size
}
object Stat {
def mean(xs: Seq[Number]): Number =
xs.reduce(_ + _) / xs.size
}
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] {
def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value)
def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value)
def /(other: Int) = BigDecimalNumber(value / other)
}
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] {
def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value)
def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value)
def /(other: Int) = BigDecimalNumber(value / other)
}
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] {
def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value)
def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value)
def /(other: Int) = BigDecimalNumber(value / other)
}
case class IntNumber(value: Int) extends Number[Int] {
def +(other: Number[Int]) = IntNumber(value + other.value)
def /(other: Number[Int]) = IntNumber(value / other.value)
def /(other: Int) = IntNumber(value / other)
}
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] {
def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value)
def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value)
def /(other: Int) = BigDecimalNumber(value / other)
}
case class IntNumber(value: Int) extends Number[Int] {
def +(other: Number[Int]) = IntNumber(value + other.value)
def /(other: Number[Int]) = IntNumber(value / other.value)
def /(other: Int) = IntNumber(value / other)
}
case class DoubleNumber(value: Double) extends Number[Double] {
def +(other: Number[Double]) = DoubleNumber(value + other.value)
def /(other: Number[Double]) = DoubleNumber(value / other.value)
def /(other: Int) = DoubleNumber(value / other)
}
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate)))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate)))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate)))
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
[error] OrderTest.scala not found: value JsonSerializer
[error] JsonSerializer.write(order) should equal(expectedJson)
[error] ^
object JsonSerializer {
def write(order: Order): String = ...
}
I have few objects I also need to
serialize to JSON. Can you make
your code a bit more generic?
Thanks!!!
object JsonSerializer {
def write(order: Order): String = ...
}
object JsonSerializer {
def write(sth: ???): String = ...
}
object JsonSerializer {
def write(serializable: JsonSerializable) = ...
}
object JsonSerializer {
def write(serializable: JsonSerializable) = ...
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
trait JsonSerializable {
def toJson: JsonValue
}
sealed trait JsonValue
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String =
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String = json match {
case JsonObject(elems) =>
val entries = for {
(key, value) <- elems
} yield s""""$key: ${write(value)}""""
"{" + entries.mkString(", ") + "}"
case JsonArray(elems) => "[" + elems.map(write).mkString(", ") + "]"
case JsonString(value) => s""""$value""""
case JsonNumber(value) => value.toString
}
}
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String = ...
}
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String = ...
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("general"),
"products" -> JsonArray(products.map(_.toJson))
))
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case object CancelledOrder extends Order {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonString("cancelled order")
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("complex"),
"orders" -> JsonArray(orders.map(_.toJson)))
)
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("basic"),
"id" -> JsonNumber(BigDecimal(id)),
"price" -> JsonNumber(price)
))
}
sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("discounted"),
"product" -> product.toJson,
"discount" -> JsonNumber(discount)
))
}
sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable
case object OutOfStock extends Product {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonString("out of stock")
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
[info] - should serialize to json
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Our goal:
● DRY principle
● Separation of concerns
● Epic decoupling
● Clean API
● Minimal Boilerplate
Act.3: “Re-inventing the
Wheel”
trait Number[A] {
def value: A
def +(other: Number[A]): Number[A]
def /(other: Number[A]): Number[A]
def /(other: Int): Number[A]
}
case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] {
def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value)
def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value)
def /(other: Int) = BigDecimalNumber(value / other)
}
case class IntNumber(value: Int) extends Number[Int] {
def +(other: Number[Int]) = IntNumber(value + other.value)
def /(other: Number[Int]) = IntNumber(value / other.value)
def /(other: Int) = IntNumber(value / other)
}
case class DoubleNumber(value: Double) extends Number[Double] {
def +(other: Number[Double]) = DoubleNumber(value + other.value)
def /(other: Number[Double]) = DoubleNumber(value / other.value)
def /(other: Int) = DoubleNumber(value / other)
}
object Stat {
def mean[A](xs: Seq[Number[A]): Number[A] =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value
}
object Stat {
def mean[A](xs: Seq[A]): A =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A]): A =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A]): A =
xs.reduce(_ + _) / xs.size
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A]): A =
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Number[A] {
def plus(n1: A, n2: A): A
def divide(n1: A, n2: A): A
def divide(n1: A, n2: Int): A
}
trait Number[A] {
def plus(n1: A, n2: A): A
def divide(n1: A, n2: A): A
def divide(n1: A, n2: Int): A
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {
def plus(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 + n2
def divide(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 / n2
def divide(n1: BigDecimal, n2: Int): BigDecimal = n1 / n2
}
trait Number[A] {
def plus(n1: A, n2: A): A
def divide(n1: A, n2: A): A
def divide(n1: A, n2: Int): A
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {
def plus(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 + n2
def divide(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 / n2
def divide(n1: BigDecimal, n2: Int): BigDecimal = n1 / n2
}
implicit object IntNumber extends Number[Int] {
def plus(n1: Int, n2: Int): Int = n1 + n2
def divide(n1: Int, n2: Int): Int = n1 / n2
}
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))(BigDecimalNumber)
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))(BigDecimalNumber)
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {..}
implicit object IntNumber extends Number[Int] {..}
}
object Stat {
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {..}
implicit object IntNumber extends Number[Int] {..}
implicit class NumberOps[A](a: A)(implicit number: Number[A]) {
def +(other: A) = number.plus(a, other)
def /(other: A) = number.divide(a, other)
def /(other: Int) = number.divide(a, other)
}
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {..}
implicit object IntNumber extends Number[Int] {..}
implicit class NumberOps[A](a: A)(implicit number: Number[A]) {
def +(other: A) = number.plus(a, other)
def /(other: A) = number.divide(a, other)
def /(other: Int) = number.divide(a, other)
}
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
number.divide(xs.reduce(number.plus),xs.size)
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {..}
implicit object IntNumber extends Number[Int] {..}
implicit class NumberOps[A](a: A)(implicit number: Number[A]) {
def +(other: A) = number.plus(a, other)
def /(other: A) = number.divide(a, other)
def /(other: Int) = number.divide(a, other)
}
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
xs.reduce(_ + _) / xs.size
}
object Number {
implicit object BigDecimalNumber extends Number[BigDecimal] {..}
implicit object IntNumber extends Number[Int] {..}
implicit class NumberOps[A](a: A)(implicit number: Number[A]) {
def +(other: A) = number.plus(a, other)
def /(other: A) = number.divide(a, other)
def /(other: Int) = number.divide(a, other)
}
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
xs.reduce(_ + _) / xs.size
}
object Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
[info] - should serialize to json
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String = ...
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = ...
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("general"),
"products" -> JsonArray(products.map(_.toJson))
))
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
object JsonSerializer {
def write[A](a: A) =
JsonWriter.write( toJson(a))
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
trait Json[-A] {
def toJson(a: A): JsonValue
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object OrderJson {
implicit object ProductToJson extends Json[Product] {
def toJson(product: Product): JsonValue = ...
}
implicit object OrderToJson extends Json[Order] {
def toJson(order: Order): JsonValue = ...
}
}
object OrderJson {
implicit object ProductToJson extends Json[Product] {
def toJson(product: Product): JsonValue = ...
}
implicit object OrderToJson extends Json[Order] {
def toJson(order: Order): JsonValue = ...
}
}
object OrderJson {
implicit object ProductToJson extends Json[Product] {
def toJson(product: Product): JsonValue = ...
}
implicit object OrderToJson extends Json[Order] {
def toJson(order: Order): JsonValue = ...
}
}
implicit object ProductToJson extends Json[Product] {
def toJson(product: Product): JsonValue = product match {
case BasicProduct(id, price) =>
JsonObject(Map(
"type" -> JsonString("basic"),
"id" -> JsonNumber(BigDecimal(id)),
"price" -> JsonNumber(price)
))
case DiscountedProduct(product, discount) =>
JsonObject(Map(
"type" -> JsonString("discounted"),
"product" -> toJson(product),
"discount" -> JsonNumber(discount)
))
case OutOfStock() =>
JsonString("out of stock")
}
}
implicit object OrderToJson extends Json[Order] {
def toJson(order: Order): JsonValue = order match {
case GeneralOrder(products) =>
JsonObject(Map(
"type" -> JsonString("general"),
"products" -> JsonArray(products.map(ProductToJson.toJson))
))
case ComplexOrder(orders) =>
JsonObject(Map(
"type" -> JsonString("complex"),
"orders" -> JsonArray(orders.map(toJson))
))
case CancelledOrder() =>
JsonString("cancelled order")
}
}
object OrderJson {
implicit object ProductToJson extends Json[Product] {
def toJson(product: Product): JsonValue = ...
}
implicit object OrderToJson extends Json[Order] {
def toJson(order: Order): JsonValue = ...
}
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
import OrderJson._
JsonSerializer.write(order) should equal(expectedJson)
}
test("should serialize to json") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: Nil)
val expectedJson = """{
"type: "complex"",
"orders: [{
"type: "general"",
"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"
},
"cancelled order"]"
}"""
import OrderJson._
JsonSerializer.write(order) should equal(expectedJson)
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
[info] - should serialize to json
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] {
def evaluate(product: Product): BigDecimal = product match {
case BasicProduct(id, price) => price
case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount)
case OutOfStock() => BigDecimal("0.0")
}}
implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] {
def evaluate(order: Order): BigDecimal = order match {
case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + ProductEvaluate.evaluate(p)
}
case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + evaluate(o)
}
case CancelledOrder() => BigDecimal("0.0")
}}}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] {
def evaluate(product: Product): BigDecimal = product match {
case BasicProduct(id, price) => price
case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount)
case OutOfStock() => BigDecimal("0.0")
}}
implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] {
def evaluate(order: Order): BigDecimal = order match {
case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + ProductEvaluate.evaluate(p)
}
case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + evaluate(o)
}
case CancelledOrder() => BigDecimal("0.0")
}}}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] {
def evaluate(product: Product): BigDecimal = product match {
case BasicProduct(id, price) => price
case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount)
case OutOfStock() => BigDecimal("0.0")
}}
implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] {
def evaluate(order: Order): BigDecimal = order match {
case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + ProductEvaluate.evaluate(p)
}
case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + evaluate(o)
}
case CancelledOrder() => BigDecimal("0.0")
}}}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] {
def evaluate(product: Product): BigDecimal = product match {
case BasicProduct(id, price) => price
case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount)
case OutOfStock() => BigDecimal("0.0")
}}
implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] {
def evaluate(order: Order): BigDecimal = order match {
case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + ProductEvaluate.evaluate(p)
}
case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + evaluate(o)
}
case CancelledOrder() => BigDecimal("0.0")
}}}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Evaluate {
implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) {
def evaluate = ev.evaluate(a)
}
}
object Order {
import Number._
def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Evaluate {
implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) {
def evaluate = ev.evaluate(a)
}
}
object Order {
import Number._
def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Evaluate {
implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) {
def evaluate = ev.evaluate(a)
}
}
object Order {
import Number._
def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Act.4: “Enlightenment”
Research
Definitions
Definitions
● Type classes
Definitions
● Type classes
○ Ad-hoc polymorphism
● Abstract Data Type (ADT)
Our goal:
● DRY principle
● Separation of concerns
● Epic decoupling
● Clean API
● Minimal Boilerplate
Where to go next
1. Semigroup
2. Monoid
3. Functor
4. Applicative
5. Monad!
All are just type classes with some laws. That’s it!
Links & Resources
● https://github.com/rabbitonweb/scala_typeclasses
● https://inoio.de/blog/2014/07/19/type-class-101-semigroup/
● http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-
part-12-type-classes.html
● https://www.youtube.com/watch?v=sVMES4RZF-8
● http://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf
● https://www.destroyallsoftware.com/misc/reject.pdf
● http://southpark.cc.com/avatar
● http://www.tandemic.com/wp-content/uploads/Definition.png
And that’s all folks!
And that’s all folks!
Paweł Szulc
And that’s all folks!
Paweł Szulc
twitter: @rabbitonweb
And that’s all folks!
Paweł Szulc
twitter: @rabbitonweb
blog: http://rabbitonweb.com
And that’s all folks!
Paweł Szulc
twitter: @rabbitonweb
blog: http://rabbitonweb.com
github: https://github.com/rabbitonweb
And that’s all folks!
Paweł Szulc
twitter: @rabbitonweb
blog: http://rabbitonweb.com
github: https://github.com/rabbitonweb
Questions?
And that’s all folks!
Paweł Szulc
twitter: @rabbitonweb
blog: http://rabbitonweb.com
github: https://github.com/rabbitonweb
Questions?
Thank you!
Bonus!
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
xs.reduce(_ + _) / xs.size
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
xs.reduce(_ + _) / xs.size
}
object Stat {
import Number._
def mean[A : Number](xs: Seq[A]): A =
xs.reduce(_ + _) / xs.size
}
object Stat {
import Number._
def mean[A](xs: Seq[A])(implicit number: Number[A]): A =
xs.reduce(_ + _) / xs.size
}
object Stat {
import Number._
def mean[A : Number](xs: Seq[A]): A =
xs.reduce(_ + _) / xs.size
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object JsonSerializer {
def write[A : Json](a: A) =
JsonWriter.write(implicitly[Json[A]].toJson(a))
}
object JsonSerializer {
def write[A](a: A)(implicit json: Json[A]) =
JsonWriter.write(json.toJson(a))
}
object JsonSerializer {
def write[A : Json](a: A) =
JsonWriter.write(implicitly[Json[A]].toJson(a))
}

More Related Content

What's hot

Postgres rules
Postgres rulesPostgres rules
Postgres rulesgisborne
 
Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2Martin Zapletal
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2RORLAB
 
The Ring programming language version 1.5.3 book - Part 37 of 184
The Ring programming language version 1.5.3 book - Part 37 of 184The Ring programming language version 1.5.3 book - Part 37 of 184
The Ring programming language version 1.5.3 book - Part 37 of 184Mahmoud Samir Fayed
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEDarwin Durand
 
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...Ontico
 
The Ring programming language version 1.3 book - Part 28 of 88
The Ring programming language version 1.3 book - Part 28 of 88The Ring programming language version 1.3 book - Part 28 of 88
The Ring programming language version 1.3 book - Part 28 of 88Mahmoud Samir Fayed
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42Yevhen Bobrov
 
Create a Customized GMF DnD Framework
Create a Customized GMF DnD FrameworkCreate a Customized GMF DnD Framework
Create a Customized GMF DnD FrameworkKaniska Mandal
 
Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#Vagif Abilov
 
supporting t-sql scripts for Heap vs clustered table
supporting t-sql scripts for Heap vs clustered tablesupporting t-sql scripts for Heap vs clustered table
supporting t-sql scripts for Heap vs clustered tableMahabubur Rahaman
 
The Ring programming language version 1.2 book - Part 26 of 84
The Ring programming language version 1.2 book - Part 26 of 84The Ring programming language version 1.2 book - Part 26 of 84
The Ring programming language version 1.2 book - Part 26 of 84Mahmoud Samir Fayed
 
BGOUG15: JSON support in MySQL 5.7
BGOUG15: JSON support in MySQL 5.7BGOUG15: JSON support in MySQL 5.7
BGOUG15: JSON support in MySQL 5.7Georgi Kodinov
 
Performante Java Enterprise Applikationen trotz O/R-Mapping
Performante Java Enterprise Applikationen trotz O/R-MappingPerformante Java Enterprise Applikationen trotz O/R-Mapping
Performante Java Enterprise Applikationen trotz O/R-MappingSimon Martinelli
 

What's hot (19)

Postgres rules
Postgres rulesPostgres rules
Postgres rules
 
Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2Data in Motion: Streaming Static Data Efficiently 2
Data in Motion: Streaming Static Data Efficiently 2
 
ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2ActiveRecord Query Interface (2), Season 2
ActiveRecord Query Interface (2), Season 2
 
The Ring programming language version 1.5.3 book - Part 37 of 184
The Ring programming language version 1.5.3 book - Part 37 of 184The Ring programming language version 1.5.3 book - Part 37 of 184
The Ring programming language version 1.5.3 book - Part 37 of 184
 
qooxdoo Form Management
qooxdoo Form Managementqooxdoo Form Management
qooxdoo Form Management
 
VISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLEVISUALIZAR REGISTROS EN UN JTABLE
VISUALIZAR REGISTROS EN UN JTABLE
 
MaintainStaffTable
MaintainStaffTableMaintainStaffTable
MaintainStaffTable
 
Php forum2015 tomas_final
Php forum2015 tomas_finalPhp forum2015 tomas_final
Php forum2015 tomas_final
 
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
CREATE INDEX … USING VODKA. VODKA CONNECTING INDEXES, Олег Бартунов, Александ...
 
The Ring programming language version 1.3 book - Part 28 of 88
The Ring programming language version 1.3 book - Part 28 of 88The Ring programming language version 1.3 book - Part 28 of 88
The Ring programming language version 1.3 book - Part 28 of 88
 
The uniform interface is 42
The uniform interface is 42The uniform interface is 42
The uniform interface is 42
 
Create a Customized GMF DnD Framework
Create a Customized GMF DnD FrameworkCreate a Customized GMF DnD Framework
Create a Customized GMF DnD Framework
 
Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#Typed? Dynamic? Both! Cross-platform DSLs in C#
Typed? Dynamic? Both! Cross-platform DSLs in C#
 
supporting t-sql scripts for Heap vs clustered table
supporting t-sql scripts for Heap vs clustered tablesupporting t-sql scripts for Heap vs clustered table
supporting t-sql scripts for Heap vs clustered table
 
The Ring programming language version 1.2 book - Part 26 of 84
The Ring programming language version 1.2 book - Part 26 of 84The Ring programming language version 1.2 book - Part 26 of 84
The Ring programming language version 1.2 book - Part 26 of 84
 
Sql
SqlSql
Sql
 
BGOUG15: JSON support in MySQL 5.7
BGOUG15: JSON support in MySQL 5.7BGOUG15: JSON support in MySQL 5.7
BGOUG15: JSON support in MySQL 5.7
 
Performante Java Enterprise Applikationen trotz O/R-Mapping
Performante Java Enterprise Applikationen trotz O/R-MappingPerformante Java Enterprise Applikationen trotz O/R-Mapping
Performante Java Enterprise Applikationen trotz O/R-Mapping
 
Easy Button
Easy ButtonEasy Button
Easy Button
 

Viewers also liked

Real world gobbledygook
Real world gobbledygookReal world gobbledygook
Real world gobbledygookPawel Szulc
 
The cats toolbox a quick tour of some basic typeclasses
The cats toolbox  a quick tour of some basic typeclassesThe cats toolbox  a quick tour of some basic typeclasses
The cats toolbox a quick tour of some basic typeclassesPawel Szulc
 
Introduction to type classes
Introduction to type classesIntroduction to type classes
Introduction to type classesPawel Szulc
 
Apache spark when things go wrong
Apache spark   when things go wrongApache spark   when things go wrong
Apache spark when things go wrongPawel Szulc
 
Writing your own RDD for fun and profit
Writing your own RDD for fun and profitWriting your own RDD for fun and profit
Writing your own RDD for fun and profitPawel Szulc
 
Functional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heavenFunctional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heavenPawel Szulc
 
“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”Pawel Szulc
 
Apache spark workshop
Apache spark workshopApache spark workshop
Apache spark workshopPawel Szulc
 
Going bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesGoing bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesPawel Szulc
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs FreePawel Szulc
 

Viewers also liked (10)

Real world gobbledygook
Real world gobbledygookReal world gobbledygook
Real world gobbledygook
 
The cats toolbox a quick tour of some basic typeclasses
The cats toolbox  a quick tour of some basic typeclassesThe cats toolbox  a quick tour of some basic typeclasses
The cats toolbox a quick tour of some basic typeclasses
 
Introduction to type classes
Introduction to type classesIntroduction to type classes
Introduction to type classes
 
Apache spark when things go wrong
Apache spark   when things go wrongApache spark   when things go wrong
Apache spark when things go wrong
 
Writing your own RDD for fun and profit
Writing your own RDD for fun and profitWriting your own RDD for fun and profit
Writing your own RDD for fun and profit
 
Functional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heavenFunctional Programming & Event Sourcing - a pair made in heaven
Functional Programming & Event Sourcing - a pair made in heaven
 
“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”“Going bananas with recursion schemes for fixed point data types”
“Going bananas with recursion schemes for fixed point data types”
 
Apache spark workshop
Apache spark workshopApache spark workshop
Apache spark workshop
 
Going bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data typesGoing bananas with recursion schemes for fixed point data types
Going bananas with recursion schemes for fixed point data types
 
Make your programs Free
Make your programs FreeMake your programs Free
Make your programs Free
 

Similar to Introduction to type classes in 30 min

Mongo db ecommerce
Mongo db ecommerceMongo db ecommerce
Mongo db ecommercechristkv
 
The Time to Defer is Now
The Time to Defer is NowThe Time to Defer is Now
The Time to Defer is NowMichael Diamant
 
Query for json databases
Query for json databasesQuery for json databases
Query for json databasesBinh Le
 
Solid Software Design Principles
Solid Software Design PrinciplesSolid Software Design Principles
Solid Software Design PrinciplesJon Kruger
 
WorkingWithSlick2.1.0
WorkingWithSlick2.1.0WorkingWithSlick2.1.0
WorkingWithSlick2.1.0Knoldus Inc.
 
MongoDB World 2018: Keynote
MongoDB World 2018: KeynoteMongoDB World 2018: Keynote
MongoDB World 2018: KeynoteMongoDB
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196Mahmoud Samir Fayed
 
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB
 
Learning from GOOS - work in progress
Learning from GOOS - work in progressLearning from GOOS - work in progress
Learning from GOOS - work in progressOlaf Lewitz
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETTomas Jansson
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31Mahmoud Samir Fayed
 
ADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developersADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developersBartosz Kosarzycki
 
Why Scala is the better Java
Why Scala is the better JavaWhy Scala is the better Java
Why Scala is the better JavaThomas Kaiser
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecordscalaconfjp
 
Testing Without Writing Tests
Testing Without Writing TestsTesting Without Writing Tests
Testing Without Writing TestsVíctor Muñoz
 

Similar to Introduction to type classes in 30 min (20)

Mongo db ecommerce
Mongo db ecommerceMongo db ecommerce
Mongo db ecommerce
 
The Time to Defer is Now
The Time to Defer is NowThe Time to Defer is Now
The Time to Defer is Now
 
Joy of scala
Joy of scalaJoy of scala
Joy of scala
 
Query for json databases
Query for json databasesQuery for json databases
Query for json databases
 
Functional DDD
Functional DDDFunctional DDD
Functional DDD
 
Kotlin Redux
Kotlin ReduxKotlin Redux
Kotlin Redux
 
Kotlin dsl
Kotlin dslKotlin dsl
Kotlin dsl
 
Solid Software Design Principles
Solid Software Design PrinciplesSolid Software Design Principles
Solid Software Design Principles
 
Scala on Your Phone
Scala on Your PhoneScala on Your Phone
Scala on Your Phone
 
WorkingWithSlick2.1.0
WorkingWithSlick2.1.0WorkingWithSlick2.1.0
WorkingWithSlick2.1.0
 
MongoDB World 2018: Keynote
MongoDB World 2018: KeynoteMongoDB World 2018: Keynote
MongoDB World 2018: Keynote
 
The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196The Ring programming language version 1.7 book - Part 48 of 196
The Ring programming language version 1.7 book - Part 48 of 196
 
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your DataMongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
MongoDB .local Toronto 2019: Using Change Streams to Keep Up with Your Data
 
Learning from GOOS - work in progress
Learning from GOOS - work in progressLearning from GOOS - work in progress
Learning from GOOS - work in progress
 
Getting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NETGetting started with Elasticsearch and .NET
Getting started with Elasticsearch and .NET
 
The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31The Ring programming language version 1.5 book - Part 8 of 31
The Ring programming language version 1.5 book - Part 8 of 31
 
ADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developersADG Poznań - Kotlin for Android developers
ADG Poznań - Kotlin for Android developers
 
Why Scala is the better Java
Why Scala is the better JavaWhy Scala is the better Java
Why Scala is the better Java
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
 
Testing Without Writing Tests
Testing Without Writing TestsTesting Without Writing Tests
Testing Without Writing Tests
 

More from Pawel Szulc

Getting acquainted with Lens
Getting acquainted with LensGetting acquainted with Lens
Getting acquainted with LensPawel Szulc
 
Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)Pawel Szulc
 
Painless Haskell
Painless HaskellPainless Haskell
Painless HaskellPawel Szulc
 
Trip with monads
Trip with monadsTrip with monads
Trip with monadsPawel Szulc
 
Trip with monads
Trip with monadsTrip with monads
Trip with monadsPawel Szulc
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineersPawel Szulc
 
RChain - Understanding Distributed Calculi
RChain - Understanding Distributed CalculiRChain - Understanding Distributed Calculi
RChain - Understanding Distributed CalculiPawel Szulc
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineersPawel Szulc
 
Understanding distributed calculi in Haskell
Understanding distributed calculi in HaskellUnderstanding distributed calculi in Haskell
Understanding distributed calculi in HaskellPawel Szulc
 
Software engineering the genesis
Software engineering  the genesisSoftware engineering  the genesis
Software engineering the genesisPawel Szulc
 
Category theory is general abolute nonsens
Category theory is general abolute nonsensCategory theory is general abolute nonsens
Category theory is general abolute nonsensPawel Szulc
 
Fun never stops. introduction to haskell programming language
Fun never stops. introduction to haskell programming languageFun never stops. introduction to haskell programming language
Fun never stops. introduction to haskell programming languagePawel Szulc
 
Know your platform. 7 things every scala developer should know about jvm
Know your platform. 7 things every scala developer should know about jvmKnow your platform. 7 things every scala developer should know about jvm
Know your platform. 7 things every scala developer should know about jvmPawel Szulc
 
Monads asking the right question
Monads  asking the right questionMonads  asking the right question
Monads asking the right questionPawel Szulc
 
Apache Spark 101 [in 50 min]
Apache Spark 101 [in 50 min]Apache Spark 101 [in 50 min]
Apache Spark 101 [in 50 min]Pawel Szulc
 
Javascript development done right
Javascript development done rightJavascript development done right
Javascript development done rightPawel Szulc
 
Architektura to nie bzdura
Architektura to nie bzduraArchitektura to nie bzdura
Architektura to nie bzduraPawel Szulc
 
Testing and Testable Code
Testing and Testable CodeTesting and Testable Code
Testing and Testable CodePawel Szulc
 

More from Pawel Szulc (19)

Getting acquainted with Lens
Getting acquainted with LensGetting acquainted with Lens
Getting acquainted with Lens
 
Impossibility
ImpossibilityImpossibility
Impossibility
 
Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)Maintainable Software Architecture in Haskell (with Polysemy)
Maintainable Software Architecture in Haskell (with Polysemy)
 
Painless Haskell
Painless HaskellPainless Haskell
Painless Haskell
 
Trip with monads
Trip with monadsTrip with monads
Trip with monads
 
Trip with monads
Trip with monadsTrip with monads
Trip with monads
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
 
RChain - Understanding Distributed Calculi
RChain - Understanding Distributed CalculiRChain - Understanding Distributed Calculi
RChain - Understanding Distributed Calculi
 
Illogical engineers
Illogical engineersIllogical engineers
Illogical engineers
 
Understanding distributed calculi in Haskell
Understanding distributed calculi in HaskellUnderstanding distributed calculi in Haskell
Understanding distributed calculi in Haskell
 
Software engineering the genesis
Software engineering  the genesisSoftware engineering  the genesis
Software engineering the genesis
 
Category theory is general abolute nonsens
Category theory is general abolute nonsensCategory theory is general abolute nonsens
Category theory is general abolute nonsens
 
Fun never stops. introduction to haskell programming language
Fun never stops. introduction to haskell programming languageFun never stops. introduction to haskell programming language
Fun never stops. introduction to haskell programming language
 
Know your platform. 7 things every scala developer should know about jvm
Know your platform. 7 things every scala developer should know about jvmKnow your platform. 7 things every scala developer should know about jvm
Know your platform. 7 things every scala developer should know about jvm
 
Monads asking the right question
Monads  asking the right questionMonads  asking the right question
Monads asking the right question
 
Apache Spark 101 [in 50 min]
Apache Spark 101 [in 50 min]Apache Spark 101 [in 50 min]
Apache Spark 101 [in 50 min]
 
Javascript development done right
Javascript development done rightJavascript development done right
Javascript development done right
 
Architektura to nie bzdura
Architektura to nie bzduraArchitektura to nie bzdura
Architektura to nie bzdura
 
Testing and Testable Code
Testing and Testable CodeTesting and Testable Code
Testing and Testable Code
 

Recently uploaded

Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVshikhaohhpro
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdfWave PLM
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxbodapatigopi8531
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comFatema Valibhai
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️anilsa9823
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsAlberto González Trastoy
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AIABDERRAOUF MEHENNI
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...kellynguyen01
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Steffen Staab
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceanilsa9823
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfkalichargn70th171
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Modelsaagamshah0812
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfkalichargn70th171
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...ICS
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...Health
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️Delhi Call girls
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...MyIntelliSource, Inc.
 

Recently uploaded (20)

Optimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTVOptimizing AI for immediate response in Smart CCTV
Optimizing AI for immediate response in Smart CCTV
 
5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf5 Signs You Need a Fashion PLM Software.pdf
5 Signs You Need a Fashion PLM Software.pdf
 
Hand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptxHand gesture recognition PROJECT PPT.pptx
Hand gesture recognition PROJECT PPT.pptx
 
HR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.comHR Software Buyers Guide in 2024 - HRSoftware.com
HR Software Buyers Guide in 2024 - HRSoftware.com
 
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online  ☂️
CALL ON ➥8923113531 🔝Call Girls Kakori Lucknow best sexual service Online ☂️
 
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time ApplicationsUnveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
Unveiling the Tech Salsa of LAMs with Janus in Real-Time Applications
 
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AISyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
SyndBuddy AI 2k Review 2024: Revolutionizing Content Syndication with AI
 
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
Short Story: Unveiling the Reasoning Abilities of Large Language Models by Ke...
 
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS LiveVip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
Vip Call Girls Noida ➡️ Delhi ➡️ 9999965857 No Advance 24HRS Live
 
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
Shapes for Sharing between Graph Data Spaces - and Epistemic Querying of RDF-...
 
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female serviceCALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
CALL ON ➥8923113531 🔝Call Girls Badshah Nagar Lucknow best Female service
 
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdfThe Ultimate Test Automation Guide_ Best Practices and Tips.pdf
The Ultimate Test Automation Guide_ Best Practices and Tips.pdf
 
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICECHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
CHEAP Call Girls in Pushp Vihar (-DELHI )🔝 9953056974🔝(=)/CALL GIRLS SERVICE
 
Unlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language ModelsUnlocking the Future of AI Agents with Large Language Models
Unlocking the Future of AI Agents with Large Language Models
 
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdfLearn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
Learn the Fundamentals of XCUITest Framework_ A Beginner's Guide.pdf
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
 
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
+971565801893>>SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHAB...
 
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
call girls in Vaishali (Ghaziabad) 🔝 >༒8448380779 🔝 genuine Escort Service 🔝✔️✔️
 
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
Steps To Getting Up And Running Quickly With MyTimeClock Employee Scheduling ...
 

Introduction to type classes in 30 min

  • 1. Having a cake and eating it too. Introduction to Type classes (in Scala)
  • 2.
  • 3. “The Story of a Blackout” In 4 acts
  • 4. Act.1: “Loss & Grief”
  • 5.
  • 6.
  • 7.
  • 8.
  • 9. 503 Service Temporarily Unavailable nginx
  • 10. 503 Service Temporarily Unavailable nginx
  • 11.
  • 12.
  • 13. The 5 Stages of Loss and Grief
  • 14. The 5 Stages of Loss and Grief 1. Denial
  • 15. The 5 Stages of Loss and Grief 1. Denial 2. Anger
  • 16. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining
  • 17. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression
  • 18. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression 5. Acceptance
  • 19.
  • 24. Domain & Feature Requests Domain: ● Users place Orders in the auction system Order
  • 25. Domain & Feature Requests Domain: ● Users place Orders in the auction system ● Orders can be: ○ General - contain list of Products ○ Complex - combined from two or more other Orders ○ Cancelled - used to be fully fledged Orders, but now are cancelled Order General Complex Cancelled
  • 26. Domain & Feature Requests Domain: ● Users place Orders in the auction system ● Orders can be: ○ General - contain list of Products ○ Complex - combined from two or more other Orders ○ Cancelled - used to be fully fledged Orders, but now are cancelled ● Products can be: ○ Basic - id & price ○ Discounted - wrapped Product & discount ○ OutOfStock - used to be in warehouse, but now gone (sorry) Order General Complex Cancelled Product Basic Discounter Out of Stock
  • 27. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 28. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 29. sealed trait Order case class GeneralOrder(products: List[Product]) extends Order case object CancelledOrder extends Order case class ComplexOrder(orders: List[Order]) extends Order sealed trait Product case class BasicProduct(id: Int, price: BigDecimal) extends Product case class DiscountedProduct(product: Product, discount: Double) extends Product case object OutOfStock extends Product
  • 30. sealed trait Order case class GeneralOrder(products: List[Product]) extends Order case object CancelledOrder extends Order case class ComplexOrder(orders: List[Order]) extends Order sealed trait Product case class BasicProduct(id: Int, price: BigDecimal) extends Product case class DiscountedProduct(product: Product, discount: Double) extends Product case object OutOfStock extends Product
  • 31. sealed trait Order case class GeneralOrder(products: List[Product]) extends Order case object CancelledOrder extends Order case class ComplexOrder(orders: List[Order]) extends Order sealed trait Product case class BasicProduct(id: Int, price: BigDecimal) extends Product case class DiscountedProduct(product: Product, discount: Double) extends Product case object OutOfStock extends Product
  • 32. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 33. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 34. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 35. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 36. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 37. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 38. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 39. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) } [error] OrderTest.scala evaluate is not a member of jw.ComplexOrder [error] order.evaluate should equal (BigDecimal("11.0")) [error] ^ [error] one error found
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 47. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) } [error] OrderTest.scala evaluate is not a member of jw.ComplexOrder [error] order.evaluate should equal (BigDecimal("11.0")) [error] ^ [error] one error found
  • 48. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 49. sealed trait Order case class GeneralOrder(products: List[Product]) extends Order case object CancelledOrder extends Order case class ComplexOrder(orders: List[Order]) extends Order sealed trait Product case class BasicProduct(id: Int, price: BigDecimal) extends Product case class DiscountedProduct(product: Product, discount: Double) extends Product case object OutOfStock extends Product
  • 50. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 51. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 52. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 53. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 54. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 55. trait Evaluatable[T] { def evaluate: T } sealed trait Product extends Evaluatable[BigDecimal] case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 56. trait Evaluatable[T] { def evaluate: T } sealed trait Product extends Evaluatable[BigDecimal] case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 57. trait Evaluatable[T] { def evaluate: T } sealed trait Product extends Evaluatable[BigDecimal] case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 58. trait Evaluatable[T] { def evaluate: T } sealed trait Product extends Evaluatable[BigDecimal] case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 59. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) }
  • 60. test("should evaluate order") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = GeneralOrder(DiscountedProduct( product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o3 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: o3 :: Nil) order.evaluate should equal (BigDecimal("11.0")) } [info] OrderTest: [info] - should evaluate order
  • 61.
  • 62. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 63. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 64. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 65. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 66. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 67. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 68. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 69. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 70. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 71. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) }
  • 72. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) } [error] OrderTest.scala average is not a member of jw.Order [error] Order.average should equal (BigDecimal("5.0")) [error] ^ [error] one error found
  • 73. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = ??? }
  • 74. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 75. object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) } object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 76.
  • 77. AWESOME! I just need it to work for Doubles and Ints as well! Can you make it more generic? Thx!
  • 78. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 79. object Stat { def mean(xs: Seq[Number]): Number = xs.reduce(_ + _) / xs.size }
  • 80. object Stat { def mean(xs: Seq[Number]): Number = xs.reduce(_ + _) / xs.size }
  • 81.
  • 82.
  • 83. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] }
  • 84. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] } case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] { def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value) def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value) def /(other: Int) = BigDecimalNumber(value / other) }
  • 85. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] } case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] { def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value) def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value) def /(other: Int) = BigDecimalNumber(value / other) }
  • 86. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] } case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] { def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value) def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value) def /(other: Int) = BigDecimalNumber(value / other) } case class IntNumber(value: Int) extends Number[Int] { def +(other: Number[Int]) = IntNumber(value + other.value) def /(other: Number[Int]) = IntNumber(value / other.value) def /(other: Int) = IntNumber(value / other) }
  • 87. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] } case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] { def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value) def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value) def /(other: Int) = BigDecimalNumber(value / other) } case class IntNumber(value: Int) extends Number[Int] { def +(other: Number[Int]) = IntNumber(value + other.value) def /(other: Number[Int]) = IntNumber(value / other.value) def /(other: Int) = IntNumber(value / other) } case class DoubleNumber(value: Double) extends Number[Double] { def +(other: Number[Double]) = DoubleNumber(value + other.value) def /(other: Number[Double]) = DoubleNumber(value / other.value) def /(other: Int) = DoubleNumber(value / other) }
  • 88. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 89. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 90. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 91. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 92. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 93. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 94. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))) }
  • 95. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))) }
  • 96. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))) }
  • 97. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 98. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 99. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 100. test("should calculate average") { val o1 = GeneralOrder(DiscountedProduct(product = BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil) val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil) val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0")) } [info] OrderTest: [info] - should evaluate order [info] - should calculate average
  • 101.
  • 102. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 103. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 104. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 105. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 106. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 107. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 108. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 109. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 110. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 111. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) } [error] OrderTest.scala not found: value JsonSerializer [error] JsonSerializer.write(order) should equal(expectedJson) [error] ^
  • 112. object JsonSerializer { def write(order: Order): String = ... }
  • 113.
  • 114.
  • 115. I have few objects I also need to serialize to JSON. Can you make your code a bit more generic? Thanks!!!
  • 116. object JsonSerializer { def write(order: Order): String = ... }
  • 117. object JsonSerializer { def write(sth: ???): String = ... }
  • 118. object JsonSerializer { def write(serializable: JsonSerializable) = ... }
  • 119. object JsonSerializer { def write(serializable: JsonSerializable) = ... } trait JsonSerializable { def toJson: JsonValue }
  • 120. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 121. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 123. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue
  • 124. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue object JsonWriter { def write(json: JsonValue): String =
  • 125. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue object JsonWriter { def write(json: JsonValue): String = json match { case JsonObject(elems) => val entries = for { (key, value) <- elems } yield s""""$key: ${write(value)}"""" "{" + entries.mkString(", ") + "}" case JsonArray(elems) => "[" + elems.map(write).mkString(", ") + "]" case JsonString(value) => s""""$value"""" case JsonNumber(value) => value.toString } }
  • 126. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue object JsonWriter { def write(json: JsonValue): String = ... }
  • 127. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue object JsonWriter { def write(json: JsonValue): String = ... } trait JsonSerializable { def toJson: JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 128. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
  • 129. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
  • 130. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
  • 131. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("general"), "products" -> JsonArray(products.map(_.toJson)) )) }
  • 132. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case object CancelledOrder extends Order { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonString("cancelled order") }
  • 133. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("complex"), "orders" -> JsonArray(orders.map(_.toJson))) ) }
  • 134. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("basic"), "id" -> JsonNumber(BigDecimal(id)), "price" -> JsonNumber(price) )) }
  • 135. sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("discounted"), "product" -> product.toJson, "discount" -> JsonNumber(discount) )) }
  • 136. sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable case object OutOfStock extends Product { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonString("out of stock") }
  • 137. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) } [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 138. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 139. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 140.
  • 141.
  • 142.
  • 143.
  • 144. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 146. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] } case class BigDecimalNumber(value: BigDecimal) extends Number[BigDecimal] { def +(other: Number[BigDecimal]) = BigDecimalNumber(value + other.value) def /(other: Number[BigDecimal]) = BigDecimalNumber(value / other.value) def /(other: Int) = BigDecimalNumber(value / other) } case class IntNumber(value: Int) extends Number[Int] { def +(other: Number[Int]) = IntNumber(value + other.value) def /(other: Number[Int]) = IntNumber(value / other.value) def /(other: Int) = IntNumber(value / other) } case class DoubleNumber(value: Double) extends Number[Double] { def +(other: Number[Double]) = DoubleNumber(value + other.value) def /(other: Number[Double]) = DoubleNumber(value / other.value) def /(other: Int) = DoubleNumber(value / other) }
  • 147. object Stat { def mean[A](xs: Seq[Number[A]): Number[A] = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 148. object Stat { def mean[A](xs: Seq[A]): A = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 149. object Stat { def mean[A](xs: Seq[A]): A = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 150. object Stat { def mean[A](xs: Seq[A]): A = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 151. object Stat { def mean[A](xs: Seq[A]): A = } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 152. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 153. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 154. trait Number[A] { def plus(n1: A, n2: A): A def divide(n1: A, n2: A): A def divide(n1: A, n2: Int): A }
  • 155. trait Number[A] { def plus(n1: A, n2: A): A def divide(n1: A, n2: A): A def divide(n1: A, n2: Int): A } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] { def plus(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 + n2 def divide(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 / n2 def divide(n1: BigDecimal, n2: Int): BigDecimal = n1 / n2 }
  • 156. trait Number[A] { def plus(n1: A, n2: A): A def divide(n1: A, n2: A): A def divide(n1: A, n2: Int): A } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] { def plus(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 + n2 def divide(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 / n2 def divide(n1: BigDecimal, n2: Int): BigDecimal = n1 / n2 } implicit object IntNumber extends Number[Int] { def plus(n1: Int, n2: Int): Int = n1 + n2 def divide(n1: Int, n2: Int): Int = n1 / n2 } }
  • 157. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 158. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate))(BigDecimalNumber) }
  • 159. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate))(BigDecimalNumber) }
  • 160. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 161. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 162. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] {..} implicit object IntNumber extends Number[Int] {..} }
  • 163. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] {..} implicit object IntNumber extends Number[Int] {..} implicit class NumberOps[A](a: A)(implicit number: Number[A]) { def +(other: A) = number.plus(a, other) def /(other: A) = number.divide(a, other) def /(other: Int) = number.divide(a, other) } }
  • 164. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] {..} implicit object IntNumber extends Number[Int] {..} implicit class NumberOps[A](a: A)(implicit number: Number[A]) { def +(other: A) = number.plus(a, other) def /(other: A) = number.divide(a, other) def /(other: Int) = number.divide(a, other) } }
  • 165. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] {..} implicit object IntNumber extends Number[Int] {..} implicit class NumberOps[A](a: A)(implicit number: Number[A]) { def +(other: A) = number.plus(a, other) def /(other: A) = number.divide(a, other) def /(other: Int) = number.divide(a, other) } }
  • 166. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size } object Number { implicit object BigDecimalNumber extends Number[BigDecimal] {..} implicit object IntNumber extends Number[Int] {..} implicit class NumberOps[A](a: A)(implicit number: Number[A]) { def +(other: A) = number.plus(a, other) def /(other: A) = number.divide(a, other) def /(other: Int) = number.divide(a, other) } }
  • 167. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 168. [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 169.
  • 170.
  • 171. sealed trait JsonValue case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue case class JsonArray(elems: List[JsonValue]) extends JsonValue case class JsonString(value: String) extends JsonValue case class JsonNumber(value: BigDecimal) extends JsonValue object JsonWriter { def write(json: JsonValue): String = ... } trait JsonSerializable { def toJson: JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 172. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = ... def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("general"), "products" -> JsonArray(products.map(_.toJson)) )) }
  • 173. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 174. trait JsonSerializable { def toJson: JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 175. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 176. object JsonSerializer { def write[A](a: A) = JsonWriter.write( toJson(a)) }
  • 177. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 178. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 179. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 180. trait Json[-A] { def toJson(a: A): JsonValue } object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 181. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 182. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 183. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 184. implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = product match { case BasicProduct(id, price) => JsonObject(Map( "type" -> JsonString("basic"), "id" -> JsonNumber(BigDecimal(id)), "price" -> JsonNumber(price) )) case DiscountedProduct(product, discount) => JsonObject(Map( "type" -> JsonString("discounted"), "product" -> toJson(product), "discount" -> JsonNumber(discount) )) case OutOfStock() => JsonString("out of stock") } }
  • 185. implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = order match { case GeneralOrder(products) => JsonObject(Map( "type" -> JsonString("general"), "products" -> JsonArray(products.map(ProductToJson.toJson)) )) case ComplexOrder(orders) => JsonObject(Map( "type" -> JsonString("complex"), "orders" -> JsonArray(orders.map(toJson)) )) case CancelledOrder() => JsonString("cancelled order") } }
  • 186. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 187. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" JsonSerializer.write(order) should equal(expectedJson) }
  • 188. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" import OrderJson._ JsonSerializer.write(order) should equal(expectedJson) }
  • 189. test("should serialize to json") { val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil) val o2 = CancelledOrder val order = ComplexOrder(o1 :: o2 :: Nil) val expectedJson = """{ "type: "complex"", "orders: [{ "type: "general"", "products: [{"type: "basic"", "id: 10", "price: 10.2"}]" }, "cancelled order"]" }""" import OrderJson._ JsonSerializer.write(order) should equal(expectedJson) } [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 190.
  • 191. trait Evaluatable[T] { def evaluate: T } sealed trait Product extends Evaluatable[BigDecimal] case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 192. trait Evaluatable[T] { def evaluate: T } sealed trait Order extends Evaluatable[BigDecimal] case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } }
  • 193. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] { def evaluate(product: Product): BigDecimal = product match { case BasicProduct(id, price) => price case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount) case OutOfStock() => BigDecimal("0.0") }} implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] { def evaluate(order: Order): BigDecimal = order match { case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + ProductEvaluate.evaluate(p) } case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + evaluate(o) } case CancelledOrder() => BigDecimal("0.0") }}}
  • 194. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] { def evaluate(product: Product): BigDecimal = product match { case BasicProduct(id, price) => price case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount) case OutOfStock() => BigDecimal("0.0") }} implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] { def evaluate(order: Order): BigDecimal = order match { case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + ProductEvaluate.evaluate(p) } case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + evaluate(o) } case CancelledOrder() => BigDecimal("0.0") }}}
  • 195. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] { def evaluate(product: Product): BigDecimal = product match { case BasicProduct(id, price) => price case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount) case OutOfStock() => BigDecimal("0.0") }} implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] { def evaluate(order: Order): BigDecimal = order match { case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + ProductEvaluate.evaluate(p) } case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + evaluate(o) } case CancelledOrder() => BigDecimal("0.0") }}}
  • 196. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] { def evaluate(product: Product): BigDecimal = product match { case BasicProduct(id, price) => price case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount) case OutOfStock() => BigDecimal("0.0") }} implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] { def evaluate(order: Order): BigDecimal = order match { case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + ProductEvaluate.evaluate(p) } case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + evaluate(o) } case CancelledOrder() => BigDecimal("0.0") }}}
  • 197. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 198. trait Evaluate[-A, T] { def evaluate(a: A): T } object Evaluate { implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) { def evaluate = ev.evaluate(a) } } object Order { import Number._ def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 199. trait Evaluate[-A, T] { def evaluate(a: A): T } object Evaluate { implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) { def evaluate = ev.evaluate(a) } } object Order { import Number._ def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 200. trait Evaluate[-A, T] { def evaluate(a: A): T } object Evaluate { implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) { def evaluate = ev.evaluate(a) } } object Order { import Number._ def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 201.
  • 202. sealed trait Order case class GeneralOrder(products: List[Product]) extends Order case object CancelledOrder extends Order case class ComplexOrder(orders: List[Order]) extends Order sealed trait Product case class BasicProduct(id: Int, price: BigDecimal) extends Product case class DiscountedProduct(product: Product, discount: Double) extends Product case object OutOfStock extends Product
  • 203. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 204. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 205.
  • 210. Definitions ● Type classes ○ Ad-hoc polymorphism ● Abstract Data Type (ADT)
  • 211. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 212. Where to go next 1. Semigroup 2. Monoid 3. Functor 4. Applicative 5. Monad! All are just type classes with some laws. That’s it!
  • 213. Links & Resources ● https://github.com/rabbitonweb/scala_typeclasses ● https://inoio.de/blog/2014/07/19/type-class-101-semigroup/ ● http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala- part-12-type-classes.html ● https://www.youtube.com/watch?v=sVMES4RZF-8 ● http://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.pdf ● https://www.destroyallsoftware.com/misc/reject.pdf ● http://southpark.cc.com/avatar ● http://www.tandemic.com/wp-content/uploads/Definition.png
  • 214. And that’s all folks!
  • 215. And that’s all folks! Paweł Szulc
  • 216. And that’s all folks! Paweł Szulc twitter: @rabbitonweb
  • 217. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com
  • 218. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb
  • 219. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb Questions?
  • 220. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb Questions? Thank you!
  • 221. Bonus!
  • 222. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size }
  • 223. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size } object Stat { import Number._ def mean[A : Number](xs: Seq[A]): A = xs.reduce(_ + _) / xs.size }
  • 224. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size } object Stat { import Number._ def mean[A : Number](xs: Seq[A]): A = xs.reduce(_ + _) / xs.size }
  • 225. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 226. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) } object JsonSerializer { def write[A : Json](a: A) = JsonWriter.write(implicitly[Json[A]].toJson(a)) }
  • 227. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) } object JsonSerializer { def write[A : Json](a: A) = JsonWriter.write(implicitly[Json[A]].toJson(a)) }