范畴论
范畴论
Fànchóu lùn
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
General nonsense (math.)
Abstract nonsense
From Wikipedia, the free encyclopedia
In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by
some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly
speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a
result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such
abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract
nonsense” as a light-hearted way of alerting people to their abstract nature.
General nonsense (math.)
Abstract nonsense
From Wikipedia, the free encyclopedia
In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by
some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly
speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a
result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such
abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract
nonsense” as a light-hearted way of alerting people to their abstract nature.
General nonsense (math.)
Abstract nonsense
From Wikipedia, the free encyclopedia
In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by
some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly
speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a
result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such
abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract
nonsense” as a light-hearted way of alerting people to their abstract nature.
General nonsense (math.)
Abstract nonsense
From Wikipedia, the free encyclopedia
In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by
some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly
speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a
result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such
abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract
nonsense” as a light-hearted way of alerting people to their abstract nature.
General nonsense (math.)
Abstract nonsense
From Wikipedia, the free encyclopedia
In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by
some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly
speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a
result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such
abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract
nonsense” as a light-hearted way of alerting people to their abstract nature.
Category theory is absolute
general nonsense!
What is all the fuss with all those monoids
and semigroups I keep hearing about?
“Functional Programming is academic
phenomenon. Unless your product is purely
mathematical, it’s nothing but a nice idea that
has nothing to do with real world”
Why not FP?
Why not FP?
● FP might be good nice academic exercise, but ..
Why not FP?
● FP might be good nice academic exercise, but ..
● .. has nothing to do with real world
Why not FP?
● FP might be good nice academic exercise, but ..
● .. has nothing to do with real world
● The last three decades proved that OO-programming is
useful & necessary
Why not FP?
● FP might be good nice academic exercise, but ..
● .. has nothing to do with real world
● The last three decades proved that OO-programming is
useful & necessary
● OO has some inconveniences, but it is a de facto standard
Why not FP?
● FP might be good nice academic exercise, but ..
● .. has nothing to do with real world
● The last three decades proved that OO-programming is
useful & necessary
● OO has some inconveniences, but it is a de facto standard
● FP is complex, I don’t understand it, I doubt it will be
applicable to real world, ever!
Category theory is general abolute nonsens
Edsger Dijkstra
Edsger Dijkstra
ˈɛtsxər ˈdɛɪkstra
Edsger Dijkstra
ˈɛtsxər ˈdɛɪkstra
https://en.wikipedia.org/wiki/Edsger_W._Dijkstra
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
https://xkcd.com/292/
Category theory is general abolute nonsens
Category theory is general abolute nonsens
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...)
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...) The author is a proponent of the socalled “structured
programming” style, in which, if I get it right, gotos are replaced by
indentation.
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...) The author is a proponent of the socalled “structured
programming” style, in which, if I get it right, gotos are replaced by
indentation. Structured programming is a nice academic exercise, which
works well for small examples, but I doubt that any real-world program
will ever be written in such a style.
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...) The author is a proponent of the socalled “structured
programming” style, in which, if I get it right, gotos are replaced by
indentation. Structured programming is a nice academic exercise, which
works well for small examples, but I doubt that any real-world program
will ever be written in such a style. More than 10 years of industrial
experience with Fortran have proved conclusively to everybody
concerned that, in the real world, the goto is useful and necessary:
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...) The author is a proponent of the socalled “structured
programming” style, in which, if I get it right, gotos are replaced by
indentation. Structured programming is a nice academic exercise, which
works well for small examples, but I doubt that any real-world program
will ever be written in such a style. More than 10 years of industrial
experience with Fortran have proved conclusively to everybody
concerned that, in the real world, the goto is useful and necessary: its
presence might cause some inconveniences in debugging, but it is a de
facto standard and we must live with it.
This paper tries to convince us that the well-known goto statement
should be eliminated from our programming languages or, at least (since
I don’t think that it will ever be eliminated), that programmers should
not use it. (...) The author is a proponent of the socalled “structured
programming” style, in which, if I get it right, gotos are replaced by
indentation. Structured programming is a nice academic exercise, which
works well for small examples, but I doubt that any real-world program
will ever be written in such a style. More than 10 years of industrial
experience with Fortran have proved conclusively to everybody
concerned that, in the real world, the goto is useful and necessary: its
presence might cause some inconveniences in debugging, but it is a de
facto standard and we must live with it. It will take more than the
academic elucubrations of a purist to remove it from our languages. (...)
“Semigroup, monoid, typeclass…
It sounds so abstract”
“Semigroup, monoid, typeclass…
It sounds so abstract”
“Yes, but so do all OO-patterns!”
Category theory is general abolute nonsens
“The Story of
a Blackout”
In 5 acts
Act.1: “Loss & Grief”
Category theory is general abolute nonsens
Our goal:
● DRY principle
● Separation of concerns
● Epic decoupling
● Clean API
● Minimal Boilerplate
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
503 Service Temporarily Unavailable
nginx
503 Service Temporarily Unavailable
nginx
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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
Category theory is general abolute nonsens
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
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
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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 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
Category theory is general abolute nonsens
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"))
}
[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 =
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
}
Category theory is general abolute nonsens
AWESOME!
I just need it to work for
Doubles and Ints as
well! Can you make it
more generic?
object Stat {
def mean(xs: Seq[BigDecimal]): BigDecimal =
xs.reduce(_ + _) / xs.size
}
object Stat {
def mean(xs: Seq[Number]): Number =
xs.reduce(_ + _) / xs.size
}
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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(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
Category theory is general abolute nonsens
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)
}
[error] OrderTest.scala not found: value JsonSerializer
[error] JsonSerializer.write(order) should equal(expectedJson)
[error] ^
object JsonSerializer {
def write(order: Order): String = ...
}
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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 {
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 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 = json match {
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 = json match {
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 = 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 {
def evaluate: BigDecimal
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("general"),
"products" -> JsonArray(products.map(_.toJson))
))
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
def toJson: JsonValue = JsonString("cancelled order")
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
override def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("complex"),
"orders" -> JsonArray(orders.map(_.toJson)))
)
}
sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("basic"),
"id" -> JsonNumber(BigDecimal(id)),
"price" -> JsonNumber(price)
))
}
sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
def toJson: JsonValue = JsonObject(Map(
"type" -> JsonString("discounted"),
"product" -> product.toJson,
"discount" -> JsonNumber(discount)
))
}
sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable {
def evaluate: BigDecimal
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
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
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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 Order {
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value
}
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[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])(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])(BigDecimalNumber): 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
}
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])(BigDecimalNumber): 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))
}
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))
}
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 {
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 =
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 =
xs.reduce(_ + _) / xs.size
}
object Order {
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
Category theory is general abolute nonsens
Category theory is general abolute nonsens
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 = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
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)
}
trait Json[-A] {
def toJson(a: A): JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
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 = ...
}
}
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"]"
}"""
JsonSerializer.write(order) should equal(expectedJson)
}
[error] OrderTest.scala could not find implicit value for parameter json: json.Json[jw.
ComplexOrder]
[error] 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"]"
}"""
import OrderJson._
JsonSerializer.write(order) should equal(expectedJson)
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
[info] - should serialize to json
Category theory is general abolute nonsens
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 class OutOfStock() extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
sealed trait Order extends Evaluatable[BigDecimal]
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
case class 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
}
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 Evaluate {
implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) {
def evaluate = ev.evaluate(a)
}
}
import Evaluate._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
Category theory is general abolute nonsens
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
Act.4: “Renaissance”
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
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) { ?? }
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc + o
}
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc + o
}
trait Addable[A] {
def add(a1: A, a2: A): A
}
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc + o
}
trait Addable[A] {
def add(a1: A, a2: A): A
}
implicit object OrderAddable extends Addable[Order] {
def add(o1: Order, o2: Order): Order = (o1, o2) match {
case (CancelledOrder(), o2) => o2
case (o1, CancelledOrder()) => o1
case (GeneralOrder(ps1), GeneralOrder(ps2)) => GeneralOrder(ps1 ++ ps2)
case (ComplexOrder(os1), ComplexOrder(os2)) => ComplexOrder(os1 ++ os2)
case (o1, ComplexOrder(orders)) => ComplexOrder(o1 :: orders)
case (ComplexOrder(orders), o2) => ComplexOrder(o2 :: orders)
}
}
import Addable._
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc + o
}
trait Addable[A] {
def add(a1: A, a2: A): A
}
import Addable._
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc + o
}
trait Addable[A] {
def add(a1: A, a2: A): A
}
object Addable {
implicit class AddableOps[A](a: A) {
def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other)
}
}
import Addable._
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc |+| o
}
trait Addable[A] {
def add(a1: A, a2: A): A
}
object Addable {
implicit class AddableOps[A](a: A) {
def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other)
}
}
def simplify(orders: Seq[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => acc |+| o
}
def simplify(orders: Seq[Order]): Order = fold(orders)
def simplify(orders: Seq[Order]): Order = fold(orders)
trait AddableWithZero[A] {
def zero: A
def add(a1: A, a2: A): A
}
def simplify(orders: Seq[Order]): Order = fold(orders)
trait AddableWithZero[A] {
def zero: A
def add(a1: A, a2: A): A
}
object AddableWithZero {
def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = {
values.fold(addableWithZ.zero){
case (acc, v) => addableWithZ.add(acc, v)
}
}
}
def simplify(orders: Seq[Order]): Order = fold(orders)
trait AddableWithZero[A] {
def zero: A
def add(a1: A, a2: A): A
}
def simplify(orders: Seq[Order]): Order = fold(orders)
trait AddableWithZero[A] {
def zero: A
def add(a1: A, a2: A): A
}
implicit object OrderAddableWithZero extends AddableWithZero[Order] {
def zero = CancelledOrder()
def add(o1: Order, o2: Order): Order = OrderAddable.add(o1,o2)
}
def simplify(orders: Seq[Order]): Order = fold(orders)
trait AddableWithZero[A] {
def zero: A
def add(a1: A, a2: A): A
}
object AddableWithZero {
def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = {
values.fold(addableWithZ.zero){
case (acc, v) => addableWithZ.add(acc, v)
}
}
}
Category theory is general abolute nonsens
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")
}}}
Category theory is general abolute nonsens
Act.5: “Enlightenment”
Research
Definitions
Definitions
● Type classes
Definitions
● Type classes
○ Ad-hoc polymorphism
Definitions
● Type classes
● Abstract Data Type (ADT)
Definitions
● Type classes
● Abstract Data Type (ADT)
● Addable → Semigroup
Definitions
● Type classes
● Abstract Data Type (ADT)
● Addable → Semigroup
● AddableWithZero → Monoid
Definitions
● Type classes
● Abstract Data Type (ADT)
● Addable → Semigroup
● AddableWithZero → Monoid
● Type classes come with laws!
Where to go next
1. Functor
2. Applicative
3. Monad!
All are just type classes with some laws. That’s it!
Before we go, a bit of history...
Category theory is general abolute nonsens
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?
Thank you!
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 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))
}

More Related Content

PPT
22 ideals
PDF
Computing under Vagueness
PDF
A hybrid approach toward Natural Language Understanding (by Daisuke Bekki)
PDF
사람들은 왜때문에 핀터레스트를 쓰는걸까? - 0912 UX 랩미팅 발제문
PDF
Category Theory for Mortal Programmers
PPTX
Pinterest SlideShare
PDF
소셜콘텐츠 마케팅 & 퍼블리싱 기업 이은콘텐츠 회사소개서
PPTX
Category theory for beginners
22 ideals
Computing under Vagueness
A hybrid approach toward Natural Language Understanding (by Daisuke Bekki)
사람들은 왜때문에 핀터레스트를 쓰는걸까? - 0912 UX 랩미팅 발제문
Category Theory for Mortal Programmers
Pinterest SlideShare
소셜콘텐츠 마케팅 & 퍼블리싱 기업 이은콘텐츠 회사소개서
Category theory for beginners

Similar to Category theory is general abolute nonsens (20)

PPTX
1 - Problem Solving and Abstraction.pptx
PDF
Section 8 Programming Style and Your Brain: Douglas Crockford
PDF
Logic and mathematics history and overview for students
PDF
Can programming be liberated from the von neumman style
PDF
From Computing Machineries to Cloud Computing The Minimal Levels of Abstracti...
KEY
Douglas Crockford - Programming Style and Your Brain
PDF
Dealing with negative results
PDF
Algorithmic Puzzles.pdf
PDF
Algorithmic Puzzles [Levitin & Levitin 2011-10-14].pdf
PDF
Idiomatic Python
PPTX
Class 34: Proving Unprovability
PDF
Dealing with negative results
PDF
Advantages And Disadvantages Of WBT
PPTX
Lecture 2: Language
PDF
0764141392
PDF
How not to be wrong. The power of mathematical thinking.pdf
PDF
Lecture1webhand
PDF
Can Programming Be Liberated from the von Neumann Style? by John Backus
1 - Problem Solving and Abstraction.pptx
Section 8 Programming Style and Your Brain: Douglas Crockford
Logic and mathematics history and overview for students
Can programming be liberated from the von neumman style
From Computing Machineries to Cloud Computing The Minimal Levels of Abstracti...
Douglas Crockford - Programming Style and Your Brain
Dealing with negative results
Algorithmic Puzzles.pdf
Algorithmic Puzzles [Levitin & Levitin 2011-10-14].pdf
Idiomatic Python
Class 34: Proving Unprovability
Dealing with negative results
Advantages And Disadvantages Of WBT
Lecture 2: Language
0764141392
How not to be wrong. The power of mathematical thinking.pdf
Lecture1webhand
Can Programming Be Liberated from the von Neumann Style? by John Backus
Ad

More from Pawel Szulc (20)

PDF
Getting acquainted with Lens
PDF
Impossibility
PDF
Maintainable Software Architecture in Haskell (with Polysemy)
PDF
Painless Haskell
PDF
Trip with monads
PDF
Trip with monads
PDF
Illogical engineers
PDF
RChain - Understanding Distributed Calculi
PDF
Illogical engineers
PDF
Understanding distributed calculi in Haskell
PDF
Software engineering the genesis
PDF
Make your programs Free
PDF
Going bananas with recursion schemes for fixed point data types
PDF
“Going bananas with recursion schemes for fixed point data types”
PDF
Writing your own RDD for fun and profit
PDF
The cats toolbox a quick tour of some basic typeclasses
PDF
Introduction to type classes
PDF
Functional Programming & Event Sourcing - a pair made in heaven
PDF
Apache spark workshop
PDF
Introduction to type classes in 30 min
Getting acquainted with Lens
Impossibility
Maintainable Software Architecture in Haskell (with Polysemy)
Painless Haskell
Trip with monads
Trip with monads
Illogical engineers
RChain - Understanding Distributed Calculi
Illogical engineers
Understanding distributed calculi in Haskell
Software engineering the genesis
Make your programs Free
Going bananas with recursion schemes for fixed point data types
“Going bananas with recursion schemes for fixed point data types”
Writing your own RDD for fun and profit
The cats toolbox a quick tour of some basic typeclasses
Introduction to type classes
Functional Programming & Event Sourcing - a pair made in heaven
Apache spark workshop
Introduction to type classes in 30 min
Ad

Recently uploaded (20)

PPTX
Plex Media Server 1.28.2.6151 With Crac5 2022 Free .
PDF
Building an Inclusive Web Accessibility Made Simple with Accessibility Analyzer
PPTX
Lesson-3-Operation-System-Support.pptx-I
PDF
Workplace Software and Skills - OpenStax
PPTX
string python Python Strings: Literals, Slicing, Methods, Formatting, and Pra...
PDF
Internet Download Manager IDM Crack powerful download accelerator New Version...
PDF
PDF-XChange Editor Plus 10.7.0.398.0 Crack Free Download Latest 2025
PPTX
Chapter 1 - Transaction Processing and Mgt.pptx
PDF
Lumion Pro Crack New latest version Download 2025
PPTX
DevOpsDays Halifax 2025 - Building 10x Organizations Using Modern Productivit...
PDF
CapCut PRO for PC Crack New Download (Fully Activated 2025)
PDF
MAGIX Sound Forge Pro CrackSerial Key Keygen
PDF
AI-Powered Fuzz Testing: The Future of QA
PPTX
ROI Analysis for Newspaper Industry with Odoo ERP
PDF
Top 10 Project Management Software for Small Teams in 2025.pdf
PDF
Understanding the Need for Systemic Change in Open Source Through Intersectio...
PDF
Engineering Document Management System (EDMS)
PPTX
Human-Computer Interaction for Lecture 2
PPTX
Folder Lock 10.1.9 Crack With Serial Key
PDF
Sanket Mhaiskar Resume - Senior Software Engineer (Backend, AI)
Plex Media Server 1.28.2.6151 With Crac5 2022 Free .
Building an Inclusive Web Accessibility Made Simple with Accessibility Analyzer
Lesson-3-Operation-System-Support.pptx-I
Workplace Software and Skills - OpenStax
string python Python Strings: Literals, Slicing, Methods, Formatting, and Pra...
Internet Download Manager IDM Crack powerful download accelerator New Version...
PDF-XChange Editor Plus 10.7.0.398.0 Crack Free Download Latest 2025
Chapter 1 - Transaction Processing and Mgt.pptx
Lumion Pro Crack New latest version Download 2025
DevOpsDays Halifax 2025 - Building 10x Organizations Using Modern Productivit...
CapCut PRO for PC Crack New Download (Fully Activated 2025)
MAGIX Sound Forge Pro CrackSerial Key Keygen
AI-Powered Fuzz Testing: The Future of QA
ROI Analysis for Newspaper Industry with Odoo ERP
Top 10 Project Management Software for Small Teams in 2025.pdf
Understanding the Need for Systemic Change in Open Source Through Intersectio...
Engineering Document Management System (EDMS)
Human-Computer Interaction for Lecture 2
Folder Lock 10.1.9 Crack With Serial Key
Sanket Mhaiskar Resume - Senior Software Engineer (Backend, AI)

Category theory is general abolute nonsens

  • 7. General nonsense (math.) Abstract nonsense From Wikipedia, the free encyclopedia In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.
  • 8. General nonsense (math.) Abstract nonsense From Wikipedia, the free encyclopedia In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.
  • 9. General nonsense (math.) Abstract nonsense From Wikipedia, the free encyclopedia In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.
  • 10. General nonsense (math.) Abstract nonsense From Wikipedia, the free encyclopedia In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.
  • 11. General nonsense (math.) Abstract nonsense From Wikipedia, the free encyclopedia In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.
  • 12. Category theory is absolute general nonsense! What is all the fuss with all those monoids and semigroups I keep hearing about?
  • 13. “Functional Programming is academic phenomenon. Unless your product is purely mathematical, it’s nothing but a nice idea that has nothing to do with real world”
  • 15. Why not FP? ● FP might be good nice academic exercise, but ..
  • 16. Why not FP? ● FP might be good nice academic exercise, but .. ● .. has nothing to do with real world
  • 17. Why not FP? ● FP might be good nice academic exercise, but .. ● .. has nothing to do with real world ● The last three decades proved that OO-programming is useful & necessary
  • 18. Why not FP? ● FP might be good nice academic exercise, but .. ● .. has nothing to do with real world ● The last three decades proved that OO-programming is useful & necessary ● OO has some inconveniences, but it is a de facto standard
  • 19. Why not FP? ● FP might be good nice academic exercise, but .. ● .. has nothing to do with real world ● The last three decades proved that OO-programming is useful & necessary ● OO has some inconveniences, but it is a de facto standard ● FP is complex, I don’t understand it, I doubt it will be applicable to real world, ever!
  • 32. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...)
  • 33. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...) The author is a proponent of the socalled “structured programming” style, in which, if I get it right, gotos are replaced by indentation.
  • 34. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...) The author is a proponent of the socalled “structured programming” style, in which, if I get it right, gotos are replaced by indentation. Structured programming is a nice academic exercise, which works well for small examples, but I doubt that any real-world program will ever be written in such a style.
  • 35. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...) The author is a proponent of the socalled “structured programming” style, in which, if I get it right, gotos are replaced by indentation. Structured programming is a nice academic exercise, which works well for small examples, but I doubt that any real-world program will ever be written in such a style. More than 10 years of industrial experience with Fortran have proved conclusively to everybody concerned that, in the real world, the goto is useful and necessary:
  • 36. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...) The author is a proponent of the socalled “structured programming” style, in which, if I get it right, gotos are replaced by indentation. Structured programming is a nice academic exercise, which works well for small examples, but I doubt that any real-world program will ever be written in such a style. More than 10 years of industrial experience with Fortran have proved conclusively to everybody concerned that, in the real world, the goto is useful and necessary: its presence might cause some inconveniences in debugging, but it is a de facto standard and we must live with it.
  • 37. This paper tries to convince us that the well-known goto statement should be eliminated from our programming languages or, at least (since I don’t think that it will ever be eliminated), that programmers should not use it. (...) The author is a proponent of the socalled “structured programming” style, in which, if I get it right, gotos are replaced by indentation. Structured programming is a nice academic exercise, which works well for small examples, but I doubt that any real-world program will ever be written in such a style. More than 10 years of industrial experience with Fortran have proved conclusively to everybody concerned that, in the real world, the goto is useful and necessary: its presence might cause some inconveniences in debugging, but it is a de facto standard and we must live with it. It will take more than the academic elucubrations of a purist to remove it from our languages. (...)
  • 38. “Semigroup, monoid, typeclass… It sounds so abstract”
  • 39. “Semigroup, monoid, typeclass… It sounds so abstract” “Yes, but so do all OO-patterns!”
  • 41. “The Story of a Blackout” In 5 acts
  • 42. Act.1: “Loss & Grief”
  • 44. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 49. 503 Service Temporarily Unavailable nginx
  • 50. 503 Service Temporarily Unavailable nginx
  • 53. The 5 Stages of Loss and Grief
  • 54. The 5 Stages of Loss and Grief 1. Denial
  • 55. The 5 Stages of Loss and Grief 1. Denial 2. Anger
  • 56. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining
  • 57. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression
  • 58. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression 5. Acceptance
  • 64. Domain & Feature Requests Domain: ● Users place Orders in the auction system Order
  • 65. 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
  • 66. 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
  • 67. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 68. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 69. 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
  • 70. 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")) }
  • 71. 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
  • 79. 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
  • 80. 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")) }
  • 81. 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
  • 82. 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 } }
  • 83. 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") }
  • 84. 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")) }
  • 85. 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
  • 87. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 88. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 89. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 90. 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")) }
  • 91. 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
  • 92. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 93. 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 }
  • 95. AWESOME! I just need it to work for Doubles and Ints as well! Can you make it more generic?
  • 96. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 97. object Stat { def mean(xs: Seq[Number]): Number = xs.reduce(_ + _) / xs.size }
  • 100. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] }
  • 101. 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) }
  • 102. 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) }
  • 103. 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) }
  • 104. 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) }
  • 105. 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)) }
  • 106. 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 }
  • 107. 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 }
  • 108. 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
  • 110. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 111. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 112. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 113. 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) }
  • 114. 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] ^
  • 115. object JsonSerializer { def write(order: Order): String = ... }
  • 118. I have few objects I also need to serialize to JSON. Can you make your code a bit more generic? Thanks!!!
  • 119. object JsonSerializer { def write(order: Order): String = ... }
  • 120. object JsonSerializer { def write(sth: ???): String = ... }
  • 121. object JsonSerializer { def write(serializable: JsonSerializable) = ... }
  • 122. object JsonSerializer { def write(serializable: JsonSerializable) = ... } trait JsonSerializable { def toJson: JsonValue }
  • 123. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 124. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 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
  • 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 =
  • 128. 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 {
  • 129. 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 JsonNumber(value) => value.toString } }
  • 130. 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 JsonString(value) => s""""$value"""" case JsonNumber(value) => value.toString } }
  • 131. 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 JsonArray(elems) => "[" + elems.map(write).mkString(", ") + "]" case JsonString(value) => s""""$value"""" case JsonNumber(value) => value.toString } }
  • 132. 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 } }
  • 133. 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 = ... }
  • 134. 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) }
  • 135. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal }
  • 136. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("general"), "products" -> JsonArray(products.map(_.toJson)) )) }
  • 137. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case object CancelledOrder extends Order { def evaluate: BigDecimal = BigDecimal("0.0") def toJson: JsonValue = JsonString("cancelled order") }
  • 138. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case class ComplexOrder(orders: List[Order]) extends Order { def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) { case (acc, o) => acc + o.evaluate } override def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("complex"), "orders" -> JsonArray(orders.map(_.toJson))) ) }
  • 139. sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case class BasicProduct(id: Int, price: BigDecimal) extends Product { def evaluate: BigDecimal = price def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("basic"), "id" -> JsonNumber(BigDecimal(id)), "price" -> JsonNumber(price) )) }
  • 140. sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case class DiscountedProduct(product: Product, discount: Double) extends Product { def evaluate: BigDecimal = product.evaluate * (1 - discount) def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("discounted"), "product" -> product.toJson, "discount" -> JsonNumber(discount) )) }
  • 141. sealed trait Product extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal } case object OutOfStock extends Product { def evaluate: BigDecimal = BigDecimal("0.0") def toJson: JsonValue = JsonString("out of stock") }
  • 142. 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
  • 143. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 144. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 149. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 151. 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) }
  • 152. 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 }
  • 153. object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 154. object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 155. 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)) }
  • 156. 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)) }
  • 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. trait Number[A] { def plus(n1: A, n2: A): A def divide(n1: A, n2: A): A def divide(n1: A, n2: Int): A }
  • 159. 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 }
  • 160. 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 } }
  • 161. 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)) }
  • 162. 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])(BigDecimalNumber): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 163. 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 } }
  • 164. 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])(BigDecimalNumber): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 165. 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)) }
  • 166. 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)) }
  • 167. 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) } }
  • 168. 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)) }
  • 169. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(number.plus) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 170. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 171. [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 174. 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) }
  • 175. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } def toJson: JsonValue = JsonObject(Map( "type" -> JsonString("general"), "products" -> JsonArray(products.map(_.toJson)) )) }
  • 176. 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) }
  • 177. trait JsonSerializable { def toJson: JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 178. trait Json[-A] { def toJson(a: A): JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 179. trait Json[-A] { def toJson(a: A): JsonValue } object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 180. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 181. 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") } }
  • 182. 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") } }
  • 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. 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) }
  • 185. 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 could not find implicit value for parameter json: json.Json[jw. ComplexOrder] [error] JsonSerializer.write(order) should equal(expectedJson)
  • 186. 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) }
  • 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"]" }""" import OrderJson._ JsonSerializer.write(order) should equal(expectedJson) } [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 189. 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 class OutOfStock() extends Product { def evaluate: BigDecimal = BigDecimal("0.0") }
  • 190. sealed trait Order extends Evaluatable[BigDecimal] case class GeneralOrder(products: List[Product]) extends Order { def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) { case (acc, p) => acc + p.evaluate } } case class 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 }
  • 191. 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") }}}
  • 192. 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) } } import Evaluate._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate))
  • 194. 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
  • 196. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 197. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 198. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { ?? }
  • 199. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o }
  • 200. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o } trait Addable[A] { def add(a1: A, a2: A): A }
  • 201. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o } trait Addable[A] { def add(a1: A, a2: A): A } implicit object OrderAddable extends Addable[Order] { def add(o1: Order, o2: Order): Order = (o1, o2) match { case (CancelledOrder(), o2) => o2 case (o1, CancelledOrder()) => o1 case (GeneralOrder(ps1), GeneralOrder(ps2)) => GeneralOrder(ps1 ++ ps2) case (ComplexOrder(os1), ComplexOrder(os2)) => ComplexOrder(os1 ++ os2) case (o1, ComplexOrder(orders)) => ComplexOrder(o1 :: orders) case (ComplexOrder(orders), o2) => ComplexOrder(o2 :: orders) } }
  • 202. import Addable._ def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o } trait Addable[A] { def add(a1: A, a2: A): A }
  • 203. import Addable._ def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o } trait Addable[A] { def add(a1: A, a2: A): A } object Addable { implicit class AddableOps[A](a: A) { def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other) } }
  • 204. import Addable._ def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc |+| o } trait Addable[A] { def add(a1: A, a2: A): A } object Addable { implicit class AddableOps[A](a: A) { def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other) } }
  • 205. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc |+| o }
  • 206. def simplify(orders: Seq[Order]): Order = fold(orders)
  • 207. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A }
  • 208. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A } object AddableWithZero { def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = { values.fold(addableWithZ.zero){ case (acc, v) => addableWithZ.add(acc, v) } } }
  • 209. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A }
  • 210. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A } implicit object OrderAddableWithZero extends AddableWithZero[Order] { def zero = CancelledOrder() def add(o1: Order, o2: Order): Order = OrderAddable.add(o1,o2) }
  • 211. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A } object AddableWithZero { def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = { values.fold(addableWithZ.zero){ case (acc, v) => addableWithZ.add(acc, v) } } }
  • 213. 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") }}}
  • 214. 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") }}}
  • 220. Definitions ● Type classes ○ Ad-hoc polymorphism
  • 221. Definitions ● Type classes ● Abstract Data Type (ADT)
  • 222. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup
  • 223. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup ● AddableWithZero → Monoid
  • 224. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup ● AddableWithZero → Monoid ● Type classes come with laws!
  • 225. Where to go next 1. Functor 2. Applicative 3. Monad! All are just type classes with some laws. That’s it!
  • 226. Before we go, a bit of history...
  • 228. 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
  • 229. And that’s all folks!
  • 230. And that’s all folks! Paweł Szulc
  • 231. And that’s all folks! Paweł Szulc twitter: @rabbitonweb
  • 232. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com
  • 233. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb
  • 234. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb Questions?
  • 237. object Stat { import Number._ def mean[A](xs: Seq[A])(implicit number: Number[A]): A = xs.reduce(_ + _) / xs.size }
  • 238. 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 }
  • 239. object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 240. 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)) }