category theory is general abolute nonsens

Post on 23-Jan-2018

542 Views

Category:

Software

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

范畴论

范畴论Fànchóu lùn

General nonsense (math.)

Abstract nonsenseFrom Wikipedia, the free encyclopedia

In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.

General nonsense (math.)

Abstract nonsenseFrom Wikipedia, the free encyclopedia

In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.

General nonsense (math.)

Abstract nonsenseFrom Wikipedia, the free encyclopedia

In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.

General nonsense (math.)

Abstract nonsenseFrom Wikipedia, the free encyclopedia

In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.

General nonsense (math.)

Abstract nonsenseFrom Wikipedia, the free encyclopedia

In mathematics, abstract nonsense, general abstract nonsense, and general nonsense are terms used facetiously by some mathematicians to describe certain kinds of arguments and methods related to category theory. (Very) roughly speaking, category theory is the study of the general form of mathematical theories, without regard to their content. As a result, a proof that relies on category theoretic ideas often seems slightly out of context to those who are not used to such abstraction, sometimes to the extent that it resembles a comicalnon sequitur. Such proofs are sometimes dubbed “abstract nonsense” as a light-hearted way of alerting people to their abstract nature.

Category theory is absolute general nonsense!

What is all the fuss with all those monoids and semigroups I keep hearing about?

“Functional Programming is academic phenomenon. Unless your product is purely

mathematical, it’s nothing but a nice idea that has nothing to do with real world”

Why not FP?

Why not FP?

● FP might be good nice academic exercise, but ..

Why not FP?

● FP might be good nice academic exercise, but ..● .. has nothing to do with real world

Why not FP?

● FP might be good nice academic exercise, but ..● .. has nothing to do with real world● The last three decades proved that OO-programming is

useful & necessary

Why not FP?

● FP might be good nice academic exercise, but ..● .. has nothing to do with real world● The last three decades proved that OO-programming is

useful & necessary ● OO has some inconveniences, but it is a de facto standard

Why not FP?

● FP might be good nice academic exercise, but ..● .. has nothing to do with real world● The last three decades proved that OO-programming is

useful & necessary ● OO has some inconveniences, but it is a de facto standard● FP is complex, I don’t understand it, I doubt it will be

applicable to real world, ever!

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. (...)

“Semigroup, monoid, typeclass… It sounds so abstract”

“Semigroup, monoid, typeclass… It sounds so abstract”“Yes, but so do all OO-patterns!”

“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 Unavailablenginx

503 Service Temporarily Unavailablenginx

The 5 Stages of Loss and Grief

The 5 Stages of Loss and Grief

1. Denial

The 5 Stages of Loss and Grief

1. Denial2. Anger

The 5 Stages of Loss and Grief

1. Denial2. Anger3. Bargaining

The 5 Stages of Loss and Grief

1. Denial2. Anger3. Bargaining4. Depression

The 5 Stages of Loss and Grief

1. Denial2. Anger3. Bargaining4. Depression5. Acceptance

Act.2: “The Mess”

auction

auctionify

auctionify.io

Domain & Feature RequestsDomain:

● Users place Orders in the auction system

Order

Domain & Feature RequestsDomain:

● 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 RequestsDomain:

● 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 RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature 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 RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature 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 RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature 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 RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature 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])(BigDecimalNumber): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

trait Number[A] {

def plus(n1: A, n2: A): A

def divide(n1: A, n2: A): A

def divide(n1: A, n2: Int): A

}

object Number {

implicit object BigDecimalNumber extends Number[BigDecimal] {

def plus(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 + n2

def divide(n1: BigDecimal, n2: BigDecimal): BigDecimal = n1 / n2

def divide(n1: BigDecimal, n2: Int): BigDecimal = n1 / n2

}

implicit object IntNumber extends Number[Int] {

def plus(n1: Int, n2: Int): Int = n1 + n2

def divide(n1: Int, n2: Int): Int = n1 / n2

}

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

number.divide(xs.reduce(number.plus),xs.size)

}

object Order {

def average(orders: Seq[Order])(BigDecimalNumber): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

number.divide(xs.reduce(number.plus),xs.size)

}

object Order {

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

number.divide(xs.reduce(number.plus),xs.size)

}

object Order {

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

implicit class NumberOps[A](a: A)(implicit number: Number[A]) {

def +(other: A) = number.plus(a, other)

def /(other: A) = number.divide(a, other)

def /(other: Int) = number.divide(a, other)

}

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

number.divide(xs.reduce(number.plus),xs.size)

}

object Order {

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

xs.reduce(number.plus) / xs.size

}

object Order {

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

object Stat {

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

xs.reduce(_ + _) / xs.size

}

object Order {

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

}

[info] OrderTest:

[info] - should evaluate order

[info] - should calculate average

[info] - should serialize to json

sealed trait JsonValue

case class JsonObject(elem: Map[String, JsonValue]) extends JsonValue

case class JsonArray(elems: List[JsonValue]) extends JsonValue

case class JsonString(value: String) extends JsonValue

case class JsonNumber(value: BigDecimal) extends JsonValue

object JsonWriter {

def write(json: JsonValue): String = ...

}

trait JsonSerializable {

def toJson: JsonValue

}

object JsonSerializer {

def write(serializable: JsonSerializable) =

JsonWriter.write(serializable.toJson)

}

sealed trait Order extends Evaluatable[BigDecimal] with JsonSerializable

case class GeneralOrder(products: List[Product]) extends Order {

def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {

case (acc, p) => acc + p.evaluate

}

def toJson: JsonValue = JsonObject(Map(

"type" -> JsonString("general"),

"products" -> JsonArray(products.map(_.toJson))

))

}

test("should serialize to json") {

val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)

val o2 = CancelledOrder

val order = ComplexOrder(o1 :: o2 :: Nil)

val expectedJson = """{

"type: "complex"",

"orders: [{

"type: "general"",

"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"

},

"cancelled order"]"

}"""

JsonSerializer.write(order) should equal(expectedJson)

}

trait JsonSerializable {

def toJson: JsonValue

}

object JsonSerializer {

def write(serializable: JsonSerializable) =

JsonWriter.write(serializable.toJson)

}

trait Json[-A] {

def toJson(a: A): JsonValue

}

object JsonSerializer {

def write(serializable: JsonSerializable) =

JsonWriter.write(serializable.toJson)

}

trait Json[-A] {

def toJson(a: A): JsonValue

}

object JsonSerializer {

def write[A](a: A)(implicit json: Json[A]) =

JsonWriter.write(json.toJson(a))

}

object OrderJson {

implicit object ProductToJson extends Json[Product] {

def toJson(product: Product): JsonValue = ...

}

implicit object OrderToJson extends Json[Order] {

def toJson(order: Order): JsonValue = ...

}

}

implicit object ProductToJson extends Json[Product] {

def toJson(product: Product): JsonValue = product match {

case BasicProduct(id, price) =>

JsonObject(Map(

"type" -> JsonString("basic"),

"id" -> JsonNumber(BigDecimal(id)),

"price" -> JsonNumber(price)

))

case DiscountedProduct(product, discount) =>

JsonObject(Map(

"type" -> JsonString("discounted"),

"product" -> toJson(product),

"discount" -> JsonNumber(discount)

))

case OutOfStock() =>

JsonString("out of stock")

}

}

implicit object OrderToJson extends Json[Order] {

def toJson(order: Order): JsonValue = order match {

case GeneralOrder(products) =>

JsonObject(Map(

"type" -> JsonString("general"),

"products" -> JsonArray(products.map(ProductToJson.toJson))

))

case ComplexOrder(orders) =>

JsonObject(Map(

"type" -> JsonString("complex"),

"orders" -> JsonArray(orders.map(toJson))

))

case CancelledOrder() =>

JsonString("cancelled order")

}

}

implicit object OrderToJson extends Json[Order] {

def toJson(order: Order): JsonValue = order match {

case GeneralOrder(products) =>

JsonObject(Map(

"type" -> JsonString("general"),

"products" -> JsonArray(products.map(ProductToJson.toJson))

))

case ComplexOrder(orders) =>

JsonObject(Map(

"type" -> JsonString("complex"),

"orders" -> JsonArray(orders.map(toJson))

))

case CancelledOrder() =>

JsonString("cancelled order")

}

}

object OrderJson {

implicit object ProductToJson extends Json[Product] {

def toJson(product: Product): JsonValue = ...

}

implicit object OrderToJson extends Json[Order] {

def toJson(order: Order): JsonValue = ...

}

}

test("should serialize to json") {

val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)

val o2 = CancelledOrder

val order = ComplexOrder(o1 :: o2 :: Nil)

val expectedJson = """{

"type: "complex"",

"orders: [{

"type: "general"",

"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"

},

"cancelled order"]"

}"""

JsonSerializer.write(order) should equal(expectedJson)

}

test("should serialize to json") {

val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)

val o2 = CancelledOrder

val order = ComplexOrder(o1 :: o2 :: Nil)

val expectedJson = """{

"type: "complex"",

"orders: [{

"type: "general"",

"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"

},

"cancelled order"]"

}"""

JsonSerializer.write(order) should equal(expectedJson)

}

[error] OrderTest.scala could not find implicit value for parameter json: json.Json[jw.

ComplexOrder]

[error] JsonSerializer.write(order) should equal(expectedJson)

test("should serialize to json") {

val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)

val o2 = CancelledOrder

val order = ComplexOrder(o1 :: o2 :: Nil)

val expectedJson = """{

"type: "complex"",

"orders: [{

"type: "general"",

"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"

},

"cancelled order"]"

}"""

JsonSerializer.write(order) should equal(expectedJson)

}

test("should serialize to json") {

val o1 = GeneralOrder(BasicProduct(10, BigDecimal("10.2")) :: Nil)

val o2 = CancelledOrder

val order = ComplexOrder(o1 :: o2 :: Nil)

val expectedJson = """{

"type: "complex"",

"orders: [{

"type: "general"",

"products: [{"type: "basic"", "id: 10", "price: 10.2"}]"

},

"cancelled order"]"

}"""

import OrderJson._

JsonSerializer.write(order) should equal(expectedJson)

}

[info] OrderTest:

[info] - should evaluate order

[info] - should calculate average

[info] - should serialize to json

sealed trait Product extends Evaluatable[BigDecimal]

case class BasicProduct(id: Int, price: BigDecimal) extends Product {

def evaluate: BigDecimal = price

}

case class DiscountedProduct(product: Product, discount: Double) extends Product {

def evaluate: BigDecimal = product.evaluate * (1 - discount)

}

case class OutOfStock() extends Product {

def evaluate: BigDecimal = BigDecimal("0.0")

}

sealed trait Order extends Evaluatable[BigDecimal]

case class GeneralOrder(products: List[Product]) extends Order {

def evaluate: BigDecimal = products.foldLeft(BigDecimal("0.0")) {

case (acc, p) => acc + p.evaluate

}

}

case class CancelledOrder() extends Order {

def evaluate: BigDecimal = BigDecimal("0.0")

}

case class ComplexOrder(orders: List[Order]) extends Order {

def evaluate: BigDecimal = orders.foldLeft(BigDecimal("0.0")) {

case (acc, o) => acc + o.evaluate

}

trait Evaluate[-A, T] { def evaluate(a: A): T }

object Order {

implicit object ProductEvaluate extends Evaluate[Product, BigDecimal] {

def evaluate(product: Product): BigDecimal = product match {

case BasicProduct(id, price) => price

case DiscountedProduct(discounted, discount) => evaluate(discounted) * (1 - discount)

case OutOfStock() => BigDecimal("0.0")

}}

implicit object OrderEvaluate extends Evaluate[Order, BigDecimal] {

def evaluate(order: Order): BigDecimal = order match {

case GeneralOrder(products) => products.foldLeft(BigDecimal("0.0")) {

case (acc, p) => acc + ProductEvaluate.evaluate(p)

}

case ComplexOrder(orders) => orders.foldLeft(BigDecimal("0.0")) {

case (acc, o) => acc + evaluate(o)

}

case CancelledOrder() => BigDecimal("0.0")

}}}

trait Evaluate[-A, T] { def evaluate(a: A): T }

object Evaluate {

implicit class EvaluateOps[-A, T](a: A)(implicit ev: Evaluate[A, T]) {

def evaluate = ev.evaluate(a)

}

}

import Evaluate._

def average(orders: Seq[Order]): BigDecimal =

Stat.mean(orders.map(_.evaluate))

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 RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

Domain & Feature RequestsFeature Requests:

● Present current state of orders (JSON)● Calculate final checkout● Calculate average Order price● “Simplify” Orders

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) { ?? }

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc + o

}

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc + o

}

trait Addable[A] {

def add(a1: A, a2: A): A

}

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc + o

}

trait Addable[A] {

def add(a1: A, a2: A): A

}

implicit object OrderAddable extends Addable[Order] {

def add(o1: Order, o2: Order): Order = (o1, o2) match {

case (CancelledOrder(), o2) => o2

case (o1, CancelledOrder()) => o1

case (GeneralOrder(ps1), GeneralOrder(ps2)) => GeneralOrder(ps1 ++ ps2)

case (ComplexOrder(os1), ComplexOrder(os2)) => ComplexOrder(os1 ++ os2)

case (o1, ComplexOrder(orders)) => ComplexOrder(o1 :: orders)

case (ComplexOrder(orders), o2) => ComplexOrder(o2 :: orders)

}

}

import Addable._

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc + o

}

trait Addable[A] {

def add(a1: A, a2: A): A

}

import Addable._

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc + o

}

trait Addable[A] {

def add(a1: A, a2: A): A

}

object Addable {

implicit class AddableOps[A](a: A) {

def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other)

}

}

import Addable._

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc |+| o

}

trait Addable[A] {

def add(a1: A, a2: A): A

}

object Addable {

implicit class AddableOps[A](a: A) {

def |+|(other: A)(implicit addable: Addable[A]) = addable.add(a, other)

}

}

def simplify(orders: Seq[Order]): Order =

orders.foldLeft(CancelledOrder()) {

case (acc, o) => acc |+| o

}

def simplify(orders: Seq[Order]): Order = fold(orders)

def simplify(orders: Seq[Order]): Order = fold(orders)

trait AddableWithZero[A] {

def zero: A

def add(a1: A, a2: A): A

}

def simplify(orders: Seq[Order]): Order = fold(orders)

trait AddableWithZero[A] {

def zero: A

def add(a1: A, a2: A): A

}

object AddableWithZero {

def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = {

values.fold(addableWithZ.zero){

case (acc, v) => addableWithZ.add(acc, v)

}

}

}

def simplify(orders: Seq[Order]): Order = fold(orders)

trait AddableWithZero[A] {

def zero: A

def add(a1: A, a2: A): A

}

def simplify(orders: Seq[Order]): Order = fold(orders)

trait AddableWithZero[A] {

def zero: A

def add(a1: A, a2: A): A

}

implicit object OrderAddableWithZero extends AddableWithZero[Order] {

def zero = CancelledOrder()

def add(o1: Order, o2: Order): Order = OrderAddable.add(o1,o2)

}

def simplify(orders: Seq[Order]): Order = fold(orders)

trait AddableWithZero[A] {

def zero: A

def add(a1: A, a2: A): A

}

object AddableWithZero {

def fold[A](values: Seq[A])(implicit addableWithZ: AddableWithZero[A]): A = {

values.fold(addableWithZ.zero){

case (acc, v) => addableWithZ.add(acc, v)

}

}

}

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 next1. Functor2. Applicative3. 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!

Thank you!Bonus?

object Stat {

import Number._

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

xs.reduce(_ + _) / xs.size

}

object Stat {

import Number._

def mean[A](xs: Seq[A])(implicit number: Number[A]): A =

xs.reduce(_ + _) / xs.size

}

object Stat {

import Number._

def mean[A : Number](xs: Seq[A]): A =

xs.reduce(_ + _) / xs.size

}

object JsonSerializer {

def write[A](a: A)(implicit json: Json[A]) =

JsonWriter.write(json.toJson(a))

}

object JsonSerializer {

def write[A](a: A)(implicit json: Json[A]) =

JsonWriter.write(json.toJson(a))

}

object JsonSerializer {

def write[A : Json](a: A) =

JsonWriter.write(implicitly[Json[A]].toJson(a))

}

top related