SlideShare a Scribd company logo
1 of 249
Download to read offline
Real-World Gobbledygook
What is all the fuss with all those monoids
and semigroups I keep hearing about?
Scala?
public class MyClass {
private String someField;
public MyClass(String someField) {
this.someField = someField;
}
public String someField() {
return someField;
}
public String concat(String me) {
return someField + me;
}
public String hasCode() { .. }
public Boolean equals(Object obj) { .. }
}
public class MyClass {
private String someField;
public MyClass(String someField) {
this.someField = someField;
}
public String someField() {
return someField;
}
public String concat(String me) {
return someField + me;
}
public String hasCode() { .. }
public Boolean equals(Object obj) { .. }
}
class MyClass {
private String someField;
public MyClass(String someField) {
this.someField = someField;
}
String someField() {
return someField;
}
String concat(String me) {
return someField + me;
}
String hasCode() { .. }
Boolean equals(Object obj) { .. }
}
class MyClass {
private String someField;
public MyClass(String someField) {
this.someField = someField;
}
String someField() {
return someField;
}
String concat(String me) {
return someField + me;
}
String hasCode() { .. }
Boolean equals(Object obj) { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
String someField() {
return someField;
}
String concat(me: SomeField) {
return someField + me;
}
String hasCode() { .. }
Boolean equals(me: Object) { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
String someField() {
return someField;
}
String concat(me: SomeField) {
return someField + me;
}
String hasCode() { .. }
Boolean equals(me: Object) { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
String someField() {
return someField;
}
String concat(me: SomeField) {
return someField + me;
}
String hasCode() { .. }
Boolean equals(me: Object) { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = {
return someField;
}
def concat(me: SomeField): String = {
return someField + me;
}
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = {
return someField;
}
def concat(me: SomeField): String = {
return someField + me;
}
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = {
return someField;
}
def concat(me: SomeField): String = {
return someField + me;
}
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = {
someField;
}
def concat(me: SomeField): String = {
someField + me;
}
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = {
someField;
}
def concat(me: SomeField): String = {
someField + me;
}
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = someField;
def concat(me: SomeField): String = someField + me;
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String;
public MyClass(someField: String) {
this.someField = someField;
}
def someField(): String = someField;
def concat(me: SomeField): String = someField + me;
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String
public MyClass(someField: String) {
this.someField = someField
}
def someField(): String = someField
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass {
private someField: String
public MyClass(someField: String) {
this.someField = someField
}
def someField(): String = someField
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass(someField: String) {
def someField(): String = someField
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass(someField: String) {
def someField(): String = someField
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass(val someField: String) {
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass(val someField: String) {
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
class MyClass(val someField: String) {
def concat(me: SomeField): String = someField + me
def hasCode(): String = { .. }
def equals(me: Object): Boolean = { .. }
}
case class MyClass(someField: String) {
def concat(me: SomeField): String = someField + me
}
case class MyClass(someField: String) {
def concat(me: SomeField): String = someField + me
}
Real-World Gobbledygook
What is all the fuss with all those monoids
and semigroups I keep hearing about?
“But what if I don’t care about Functional
Programming?”
“Functional Programming is merely an
academic phenomenon.
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!
Edsger Dijkstra
Edsger Dijkstra
ˈɛtsxər ˈdɛɪkstra
Edsger Dijkstra
ˈɛtsxər ˈdɛɪkstra
https://en.wikipedia.org/wiki/Edsger_W._Dijkstra
https://xkcd.com/292/
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. (...)
“The Story of
a Blackout”
In 5 acts
Act.1: “Loss & Grief”
Our goal:
● DRY principle
● Separation of concerns
● Epic decoupling
● Clean API
● Minimal Boilerplate
503 Service Temporarily Unavailable
nginx
503 Service Temporarily Unavailable
nginx
The 5 Stages of Loss and Grief
The 5 Stages of Loss and Grief
1. Denial
The 5 Stages of Loss and Grief
1. Denial
2. Anger
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
4. Depression
The 5 Stages of Loss and Grief
1. Denial
2. Anger
3. Bargaining
4. Depression
5. Acceptance
Act.2: “The Mess”
auction
auctionify
auctionify.io
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
Order
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
● Orders can be:
○ General - contain list of Products
○ Complex - combined from two or more
other Orders
○ Cancelled - used to be fully fledged
Orders, but now are cancelled
Order
General Complex Cancelled
Domain & Feature Requests
Domain:
● Users place Orders in the auction system
● Orders can be:
○ General - contain list of Products
○ Complex - combined from two or more
other Orders
○ Cancelled - used to be fully fledged
Orders, but now are cancelled
● Products can be:
○ Basic - id & price
○ Discounted - wrapped Product &
discount
○ OutOfStock - used to be in warehouse, but
now gone (sorry)
Order
General Complex Cancelled
Product
Basic Discounter
Out of
Stock
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[error] OrderTest.scala evaluate is not a member of jw.ComplexOrder
[error] order.evaluate should equal (BigDecimal("11.0"))
[error] ^
[error] one error found
Subtype Polymorphism
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[error] OrderTest.scala evaluate is not a member of jw.ComplexOrder
[error] order.evaluate should equal (BigDecimal("11.0"))
[error] ^
[error] one error found
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
sealed trait Order
case class GeneralOrder(products: List[Product]) extends Order
case object CancelledOrder extends Order
case class ComplexOrder(orders: List[Order]) extends Order
sealed trait Product
case class BasicProduct(id: Int, price: BigDecimal) extends Product
case class DiscountedProduct(product: Product,
discount: Double) extends Product
case object OutOfStock extends Product
trait Evaluatable[T] { def evaluate: T }
sealed trait Order extends Evaluatable[BigDecimal]
case object CancelledOrder extends Order {
def evaluate: BigDecimal = BigDecimal("0.0")
}
case class ComplexOrder(orders: List[Order]) extends Order {
def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {
case (acc, o) => acc + o.evaluate
}
}
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {
case (acc, p) => acc + p.evaluate
}
}
trait Evaluatable[T] { def evaluate: T }
sealed trait Product extends Evaluatable[BigDecimal]
case class BasicProduct(id: Int, price: BigDecimal) extends Product {
def evaluate: BigDecimal = price
}
case class DiscountedProduct(product: Product, discount: Double) extends Product {
def evaluate: BigDecimal = product.evaluate * (1 - discount)
}
case object OutOfStock extends Product {
def evaluate: BigDecimal = BigDecimal("0.0")
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
test("should evaluate order") {
val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
val o2 = GeneralOrder(DiscountedProduct(
product = BasicProduct(11, BigDecimal("1")),
discount = 0.2) :: Nil)
val o3 = CancelledOrder
val order = ComplexOrder(o1 :: o2 :: o3 :: Nil)
order.evaluate should equal (BigDecimal("11.0"))
}
[info] OrderTest:
[info] - should evaluate order
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
Domain & Feature Requests
Feature Requests:
● Present current state of orders (JSON)
● Calculate final checkout
● Calculate average Order price
● “Simplify” Orders
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
test("should calculate average") {
val o1 = GeneralOrder(DiscountedProduct(product =
BasicProduct(11, BigDecimal("1")), discount = 0.2) :: Nil)
val o2 = GeneralOrder(BasicProduct(10, BigDecimal("4")) :: Nil)
val o3 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)
Order.average(o1 :: o2 :: o3 :: Nil) should equal(BigDecimal("5.0"))
}
[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
}
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
}
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
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 = ...
}
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
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]): BigDecimal =
Stat.mean(orders.map(_.evaluate))(BigDecimalNumber)
}
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
}
}
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))(BigDecimalNumber)
}
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 {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
[info] OrderTest:
[info] - should evaluate order
[info] - should calculate average
[info] - should serialize to json
sealed trait JsonValue
case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue
case class JsonArray(elems: List[JsonValue]) extends JsonValue
case class JsonString(value: String) extends JsonValue
case class JsonNumber(value: BigDecimal) extends JsonValue
object JsonWriter {
def write(json: JsonValue): String = ...
}
trait JsonSerializable {
def toJson: JsonValue
}
object JsonSerializer {
def write(serializable: JsonSerializable) =
JsonWriter.write(serializable.toJson)
}
sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable
case class GeneralOrder(products: List[Product]) extends Order {
def evaluate: BigDecimal = 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)
}
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)
}
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
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 Order {
import Number._
def average(orders: Seq[Order]): BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
import Number._
def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]: BigDecimal =
Stat.mean(orders.map(_.evaluate))
}
trait Evaluate[-A, T] { def evaluate(a: A): T }
object Order {
import Number._
def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]: BigDecimal =
Stat.mean(orders.map(o => ev.evaluate(o))
}
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)
}
}
def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => addable.add(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)
}
}
def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => addable.add(acc, o)
}
def simplify(orders: Seq[Order]): Order = fold(orders)
def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => addable.add(acc, o)
}
def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order =
orders.foldLeft(CancelledOrder()) {
case (acc, o) => addable.add(acc, o)
}
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)
}
}
}
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")
}}}
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...
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!

More Related Content

What's hot

Zhifu Ge - How To Be Weird In Ruby - With Notes
Zhifu Ge - How To Be Weird In Ruby - With NotesZhifu Ge - How To Be Weird In Ruby - With Notes
Zhifu Ge - How To Be Weird In Ruby - With Notesottawaruby
 
groovy transforms
groovy transformsgroovy transforms
groovy transformsPaul King
 
Writing SOLID code (in practice)
Writing SOLID code (in practice)Writing SOLID code (in practice)
Writing SOLID code (in practice)Tomasz Wójcik
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovyPaul King
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy PluginsPaul King
 
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf
 
Grooscript gr8conf
Grooscript gr8confGrooscript gr8conf
Grooscript gr8confGR8Conf
 
Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?Mario Camou Riveroll
 
Better DSL Support for Groovy-Eclipse
Better DSL Support for Groovy-EclipseBetter DSL Support for Groovy-Eclipse
Better DSL Support for Groovy-EclipseAndrew Eisenberg
 
The Not Java That's Not Scala
The Not Java That's Not ScalaThe Not Java That's Not Scala
The Not Java That's Not ScalaJustin Lee
 
BangaloreJUG introduction to kotlin
BangaloreJUG   introduction to kotlinBangaloreJUG   introduction to kotlin
BangaloreJUG introduction to kotlinChandra Sekhar Nayak
 
Kotlin for Android Developers - 1
Kotlin for Android Developers - 1Kotlin for Android Developers - 1
Kotlin for Android Developers - 1Mohamed Nabil, MSc.
 
Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Mohamed Nabil, MSc.
 

What's hot (20)

Zhifu Ge - How To Be Weird In Ruby - With Notes
Zhifu Ge - How To Be Weird In Ruby - With NotesZhifu Ge - How To Be Weird In Ruby - With Notes
Zhifu Ge - How To Be Weird In Ruby - With Notes
 
groovy transforms
groovy transformsgroovy transforms
groovy transforms
 
Writing SOLID code (in practice)
Writing SOLID code (in practice)Writing SOLID code (in practice)
Writing SOLID code (in practice)
 
tictactoe groovy
tictactoe groovytictactoe groovy
tictactoe groovy
 
Atlassian Groovy Plugins
Atlassian Groovy PluginsAtlassian Groovy Plugins
Atlassian Groovy Plugins
 
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume LaforgeGR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
GR8Conf 2009: What's New in Groovy 1.6? by Guillaume Laforge
 
Grooscript gr8conf
Grooscript gr8confGrooscript gr8conf
Grooscript gr8conf
 
Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?Static or Dynamic Typing? Why not both?
Static or Dynamic Typing? Why not both?
 
Guava et Lombok au Lyon JUG
Guava et Lombok au Lyon JUGGuava et Lombok au Lyon JUG
Guava et Lombok au Lyon JUG
 
Guava et Lombok au Normandy JUG
Guava et Lombok au Normandy JUGGuava et Lombok au Normandy JUG
Guava et Lombok au Normandy JUG
 
Better DSL Support for Groovy-Eclipse
Better DSL Support for Groovy-EclipseBetter DSL Support for Groovy-Eclipse
Better DSL Support for Groovy-Eclipse
 
The Not Java That's Not Scala
The Not Java That's Not ScalaThe Not Java That's Not Scala
The Not Java That's Not Scala
 
BangaloreJUG introduction to kotlin
BangaloreJUG   introduction to kotlinBangaloreJUG   introduction to kotlin
BangaloreJUG introduction to kotlin
 
Kotlin for Android Developers - 1
Kotlin for Android Developers - 1Kotlin for Android Developers - 1
Kotlin for Android Developers - 1
 
Guava et Lombok au Brezth JUG
Guava et Lombok au Brezth JUGGuava et Lombok au Brezth JUG
Guava et Lombok au Brezth JUG
 
Guava et Lombok au Lorraine JUG
Guava et Lombok au Lorraine JUGGuava et Lombok au Lorraine JUG
Guava et Lombok au Lorraine JUG
 
Dynamic Python
Dynamic PythonDynamic Python
Dynamic Python
 
Go Java, Go!
Go Java, Go!Go Java, Go!
Go Java, Go!
 
Kotlin for Android Developers - 3
Kotlin for Android Developers - 3Kotlin for Android Developers - 3
Kotlin for Android Developers - 3
 
Kotlin Types for Java Developers
Kotlin Types for Java DevelopersKotlin Types for Java Developers
Kotlin Types for Java Developers
 

Similar to 4Developers: Paweł Szulc- Real-World Gobbledygook

The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaKonrad Malawski
 
7 Sins of Java fixed in Kotlin
7 Sins of Java fixed in Kotlin7 Sins of Java fixed in Kotlin
7 Sins of Java fixed in KotlinLuca Guadagnini
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)Pavlo Baron
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadOliver Daff
 
Introduction to Scala for JCConf Taiwan
Introduction to Scala for JCConf TaiwanIntroduction to Scala for JCConf Taiwan
Introduction to Scala for JCConf TaiwanJimin Hsieh
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaDILo Surabaya
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVMAndres Almiray
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokusHamletDRC
 
ITB2019 Real World Scenarios for Modern CFML - Nolan Erck
ITB2019 Real World Scenarios for Modern CFML - Nolan ErckITB2019 Real World Scenarios for Modern CFML - Nolan Erck
ITB2019 Real World Scenarios for Modern CFML - Nolan ErckOrtus Solutions, Corp
 
Ast transformations
Ast transformationsAst transformations
Ast transformationsHamletDRC
 
A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingGarth Gilmour
 
AST Transformations
AST TransformationsAST Transformations
AST TransformationsHamletDRC
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme SwiftMovel
 
Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016DesertJames
 
Oop2010 Scala Presentation Stal
Oop2010 Scala Presentation StalOop2010 Scala Presentation Stal
Oop2010 Scala Presentation StalMichael Stal
 

Similar to 4Developers: Paweł Szulc- Real-World Gobbledygook (20)

The things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and AkkaThe things we don't see – stories of Software, Scala and Akka
The things we don't see – stories of Software, Scala and Akka
 
OOP
OOPOOP
OOP
 
7 Sins of Java fixed in Kotlin
7 Sins of Java fixed in Kotlin7 Sins of Java fixed in Kotlin
7 Sins of Java fixed in Kotlin
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
Functor, Apply, Applicative And Monad
Functor, Apply, Applicative And MonadFunctor, Apply, Applicative And Monad
Functor, Apply, Applicative And Monad
 
Java script unleashed
Java script unleashedJava script unleashed
Java script unleashed
 
Introduction to Scala for JCConf Taiwan
Introduction to Scala for JCConf TaiwanIntroduction to Scala for JCConf Taiwan
Introduction to Scala for JCConf Taiwan
 
Kotlin intro
Kotlin introKotlin intro
Kotlin intro
 
Intro to OOP
Intro to OOPIntro to OOP
Intro to OOP
 
Having Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo SurabayaHaving Fun with Kotlin Android - DILo Surabaya
Having Fun with Kotlin Android - DILo Surabaya
 
Polyglot Programming in the JVM
Polyglot Programming in the JVMPolyglot Programming in the JVM
Polyglot Programming in the JVM
 
AST Transformations at JFokus
AST Transformations at JFokusAST Transformations at JFokus
AST Transformations at JFokus
 
ITB2019 Real World Scenarios for Modern CFML - Nolan Erck
ITB2019 Real World Scenarios for Modern CFML - Nolan ErckITB2019 Real World Scenarios for Modern CFML - Nolan Erck
ITB2019 Real World Scenarios for Modern CFML - Nolan Erck
 
Object-oriented Basics
Object-oriented BasicsObject-oriented Basics
Object-oriented Basics
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
A Sceptical Guide to Functional Programming
A Sceptical Guide to Functional ProgrammingA Sceptical Guide to Functional Programming
A Sceptical Guide to Functional Programming
 
AST Transformations
AST TransformationsAST Transformations
AST Transformations
 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
 
Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016Kotlin Austin Droids April 14 2016
Kotlin Austin Droids April 14 2016
 
Oop2010 Scala Presentation Stal
Oop2010 Scala Presentation StalOop2010 Scala Presentation Stal
Oop2010 Scala Presentation Stal
 

Recently uploaded

SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphNeo4j
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticscarlostorres15106
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...shyamraj55
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationSlibray Presentation
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraDeakin University
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Scott Keck-Warren
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 3652toLead Limited
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDGMarianaLemus7
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr LapshynFwdays
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksSoftradix Technologies
 

Recently uploaded (20)

SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge GraphSIEMENS: RAPUNZEL – A Tale About Knowledge Graph
SIEMENS: RAPUNZEL – A Tale About Knowledge Graph
 
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmaticsKotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
Kotlin Multiplatform & Compose Multiplatform - Starter kit for pragmatics
 
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
Automating Business Process via MuleSoft Composer | Bangalore MuleSoft Meetup...
 
Connect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck PresentationConnect Wave/ connectwave Pitch Deck Presentation
Connect Wave/ connectwave Pitch Deck Presentation
 
Artificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning eraArtificial intelligence in the post-deep learning era
Artificial intelligence in the post-deep learning era
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024Advanced Test Driven-Development @ php[tek] 2024
Advanced Test Driven-Development @ php[tek] 2024
 
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
Transcript: #StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Pigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food ManufacturingPigging Solutions in Pet Food Manufacturing
Pigging Solutions in Pet Food Manufacturing
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptxVulnerability_Management_GRC_by Sohang Sengupta.pptx
Vulnerability_Management_GRC_by Sohang Sengupta.pptx
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
Tech-Forward - Achieving Business Readiness For Copilot in Microsoft 365
 
APIForce Zurich 5 April Automation LPDG
APIForce Zurich 5 April  Automation LPDGAPIForce Zurich 5 April  Automation LPDG
APIForce Zurich 5 April Automation LPDG
 
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
"Federated learning: out of reach no matter how close",Oleksandr Lapshyn
 
Benefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other FrameworksBenefits Of Flutter Compared To Other Frameworks
Benefits Of Flutter Compared To Other Frameworks
 

4Developers: Paweł Szulc- Real-World Gobbledygook

  • 1. Real-World Gobbledygook What is all the fuss with all those monoids and semigroups I keep hearing about?
  • 3. public class MyClass { private String someField; public MyClass(String someField) { this.someField = someField; } public String someField() { return someField; } public String concat(String me) { return someField + me; } public String hasCode() { .. } public Boolean equals(Object obj) { .. } }
  • 4. public class MyClass { private String someField; public MyClass(String someField) { this.someField = someField; } public String someField() { return someField; } public String concat(String me) { return someField + me; } public String hasCode() { .. } public Boolean equals(Object obj) { .. } }
  • 5. class MyClass { private String someField; public MyClass(String someField) { this.someField = someField; } String someField() { return someField; } String concat(String me) { return someField + me; } String hasCode() { .. } Boolean equals(Object obj) { .. } }
  • 6. class MyClass { private String someField; public MyClass(String someField) { this.someField = someField; } String someField() { return someField; } String concat(String me) { return someField + me; } String hasCode() { .. } Boolean equals(Object obj) { .. } }
  • 7. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } String someField() { return someField; } String concat(me: SomeField) { return someField + me; } String hasCode() { .. } Boolean equals(me: Object) { .. } }
  • 8. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } String someField() { return someField; } String concat(me: SomeField) { return someField + me; } String hasCode() { .. } Boolean equals(me: Object) { .. } }
  • 9. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } String someField() { return someField; } String concat(me: SomeField) { return someField + me; } String hasCode() { .. } Boolean equals(me: Object) { .. } }
  • 10. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = { return someField; } def concat(me: SomeField): String = { return someField + me; } def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 11. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = { return someField; } def concat(me: SomeField): String = { return someField + me; } def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 12. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = { return someField; } def concat(me: SomeField): String = { return someField + me; } def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 13. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = { someField; } def concat(me: SomeField): String = { someField + me; } def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 14. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = { someField; } def concat(me: SomeField): String = { someField + me; } def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 15. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = someField; def concat(me: SomeField): String = someField + me; def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 16. class MyClass { private someField: String; public MyClass(someField: String) { this.someField = someField; } def someField(): String = someField; def concat(me: SomeField): String = someField + me; def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 17. class MyClass { private someField: String public MyClass(someField: String) { this.someField = someField } def someField(): String = someField def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 18. class MyClass { private someField: String public MyClass(someField: String) { this.someField = someField } def someField(): String = someField def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 19. class MyClass(someField: String) { def someField(): String = someField def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 20. class MyClass(someField: String) { def someField(): String = someField def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 21. class MyClass(val someField: String) { def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 22. class MyClass(val someField: String) { def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 23. class MyClass(val someField: String) { def concat(me: SomeField): String = someField + me def hasCode(): String = { .. } def equals(me: Object): Boolean = { .. } }
  • 24. case class MyClass(someField: String) { def concat(me: SomeField): String = someField + me }
  • 25. case class MyClass(someField: String) { def concat(me: SomeField): String = someField + me }
  • 26. Real-World Gobbledygook What is all the fuss with all those monoids and semigroups I keep hearing about?
  • 27. “But what if I don’t care about Functional Programming?”
  • 28. “Functional Programming is merely an academic phenomenon.
  • 30. Why not FP? ● FP might be good nice academic exercise, but ..
  • 31. Why not FP? ● FP might be good nice academic exercise, but .. ● .. has nothing to do with real world
  • 32. 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
  • 33. 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
  • 34. 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!
  • 35.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 45.
  • 46.
  • 47. 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. (...)
  • 48. 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.
  • 49. 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.
  • 50. 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:
  • 51. 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.
  • 52. 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. (...)
  • 53.
  • 54. “The Story of a Blackout” In 5 acts
  • 55. Act.1: “Loss & Grief”
  • 56.
  • 57. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 58.
  • 59.
  • 60.
  • 61.
  • 62. 503 Service Temporarily Unavailable nginx
  • 63. 503 Service Temporarily Unavailable nginx
  • 64.
  • 65.
  • 66. The 5 Stages of Loss and Grief
  • 67. The 5 Stages of Loss and Grief 1. Denial
  • 68. The 5 Stages of Loss and Grief 1. Denial 2. Anger
  • 69. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining
  • 70. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression
  • 71. The 5 Stages of Loss and Grief 1. Denial 2. Anger 3. Bargaining 4. Depression 5. Acceptance
  • 72.
  • 77. Domain & Feature Requests Domain: ● Users place Orders in the auction system Order
  • 78. 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
  • 79. 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
  • 80. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 81. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 82. 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
  • 83. 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")) }
  • 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")) } [error] OrderTest.scala evaluate is not a member of jw.ComplexOrder [error] order.evaluate should equal (BigDecimal("11.0")) [error] ^ [error] one error found
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 92. 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
  • 93. 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")) }
  • 94. 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
  • 95. 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 } }
  • 96. 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") }
  • 97. 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")) }
  • 98. 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
  • 99.
  • 100. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 101. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 102. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 103. 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")) }
  • 104. 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
  • 105. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 106. 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 }
  • 107.
  • 108. AWESOME! I just need it to work for Doubles and Ints as well! Can you make it more generic?
  • 109. object Stat { def mean(xs: Seq[BigDecimal]): BigDecimal = xs.reduce(_ + _) / xs.size }
  • 110. object Stat { def mean(xs: Seq[Number]): Number = xs.reduce(_ + _) / xs.size }
  • 111.
  • 112.
  • 113. trait Number[A] { def value: A def +(other: Number[A]): Number[A] def /(other: Number[A]): Number[A] def /(other: Int): Number[A] }
  • 114. 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) }
  • 115. 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) }
  • 116. 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) }
  • 117. 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) }
  • 118. 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)) }
  • 119. 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 }
  • 120. 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 }
  • 121. 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
  • 122.
  • 123. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 124. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 125. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 126. 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) }
  • 127. 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] ^
  • 128. object JsonSerializer { def write(order: Order): String = ... }
  • 129.
  • 130.
  • 131. I have few objects I also need to serialize to JSON. Can you make your code a bit more generic? Thanks!!!
  • 132. object JsonSerializer { def write(order: Order): String = ... }
  • 133. object JsonSerializer { def write(sth: ???): String = ... }
  • 134. object JsonSerializer { def write(serializable: JsonSerializable) = ... }
  • 135. object JsonSerializer { def write(serializable: JsonSerializable) = ... } trait JsonSerializable { def toJson: JsonValue }
  • 136. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 137. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) } trait JsonSerializable { def toJson: JsonValue }
  • 139. 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
  • 140. 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 =
  • 141. 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 {
  • 142. 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 } }
  • 143. 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 } }
  • 144. 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 } }
  • 145. 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 } }
  • 146. 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 = ... }
  • 147. 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) }
  • 148. sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable { def evaluate: BigDecimal }
  • 149. 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)) )) }
  • 150. 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") }
  • 151. 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))) ) }
  • 152. 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) )) }
  • 153. 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) )) }
  • 154. 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") }
  • 155. 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
  • 156. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 157. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 158.
  • 159.
  • 160.
  • 161.
  • 162. Our goal: ● DRY principle ● Separation of concerns ● Epic decoupling ● Clean API ● Minimal Boilerplate
  • 164. 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) }
  • 165. 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 }
  • 166. object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(o => BigDecimalNumber(o.evaluate))).value }
  • 167. object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 168. 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)) }
  • 169. 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)) }
  • 170. 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)) }
  • 171. trait Number[A] { def plus(n1: A, n2: A): A def divide(n1: A, n2: A): A def divide(n1: A, n2: Int): A }
  • 172. 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 }
  • 173. 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 } }
  • 174. 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)) }
  • 175. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate))(BigDecimalNumber) }
  • 176. 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 } }
  • 177. 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 } }
  • 178. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate))(BigDecimalNumber) }
  • 179. 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)) }
  • 180. object Stat { def mean[A](xs: Seq[A])(implicit number: Number[A]): A = number.divide(xs.reduce(number.plus),xs.size) } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 181. [info] OrderTest: [info] - should evaluate order [info] - should calculate average [info] - should serialize to json
  • 182.
  • 183.
  • 184. 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) }
  • 185. 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)) )) }
  • 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. trait JsonSerializable { def toJson: JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 188. object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 189. trait Json[-A] { def toJson(a: A): JsonValue } object JsonSerializer { def write(serializable: JsonSerializable) = JsonWriter.write(serializable.toJson) }
  • 190. trait Json[-A] { def toJson(a: A): JsonValue } object JsonSerializer { def write[A](a: A)(implicit json: Json[A]) = JsonWriter.write(json.toJson(a)) }
  • 191. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 192. 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") } }
  • 193. 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") } }
  • 194. object OrderJson { implicit object ProductToJson extends Json[Product] { def toJson(product: Product): JsonValue = ... } implicit object OrderToJson extends Json[Order] { def toJson(order: Order): JsonValue = ... } }
  • 195. 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) }
  • 196. 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)
  • 197. 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) }
  • 198. 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) }
  • 199. 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
  • 200.
  • 201. 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") }
  • 202. 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 }
  • 203. 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") }}}
  • 204. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { import Number._ def average(orders: Seq[Order]): BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 205. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { import Number._ def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]: BigDecimal = Stat.mean(orders.map(_.evaluate)) }
  • 206. trait Evaluate[-A, T] { def evaluate(a: A): T } object Order { import Number._ def average(orders: Seq[Order])(implicit ev: Evaluate[Order, BigDecimal]: BigDecimal = Stat.mean(orders.map(o => ev.evaluate(o)) }
  • 207.
  • 208. 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
  • 210. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 211. Domain & Feature Requests Feature Requests: ● Present current state of orders (JSON) ● Calculate final checkout ● Calculate average Order price ● “Simplify” Orders
  • 212. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { ?? }
  • 213. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o }
  • 214. def simplify(orders: Seq[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => acc + o } trait Addable[A] { def add(a1: A, a2: A): A }
  • 215. 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) } }
  • 216. def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => addable.add(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) } }
  • 217. def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => addable.add(acc, o) }
  • 218. def simplify(orders: Seq[Order]): Order = fold(orders)
  • 219. def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => addable.add(acc, o) }
  • 220. def simplify(orders: Seq[Order])(implicit addable: Addable[Order]): Order = orders.foldLeft(CancelledOrder()) { case (acc, o) => addable.add(acc, o) }
  • 221. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A }
  • 222. 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) } } }
  • 223. def simplify(orders: Seq[Order]): Order = fold(orders) trait AddableWithZero[A] { def zero: A def add(a1: A, a2: A): A }
  • 224. 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) }
  • 225. 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) } } }
  • 226.
  • 227. 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") }}}
  • 228. 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") }}}
  • 229.
  • 234. Definitions ● Type classes ○ Ad-hoc polymorphism
  • 235. Definitions ● Type classes ● Abstract Data Type (ADT)
  • 236. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup
  • 237. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup ● AddableWithZero → Monoid
  • 238. Definitions ● Type classes ● Abstract Data Type (ADT) ● Addable → Semigroup ● AddableWithZero → Monoid ● Type classes come with laws!
  • 239. Where to go next 1. Functor 2. Applicative 3. Monad! All are just type classes with some laws. That’s it!
  • 240. Before we go, a bit of history...
  • 241.
  • 242. 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
  • 243. And that’s all folks!
  • 244. And that’s all folks! Paweł Szulc
  • 245. And that’s all folks! Paweł Szulc twitter: @rabbitonweb
  • 246. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com
  • 247. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb
  • 248. And that’s all folks! Paweł Szulc twitter: @rabbitonweb blog: http://rabbitonweb.com github: https://github.com/rabbitonweb Questions?