SlideShare una empresa de Scribd logo
1 de 85
Analyzing Functional Programs
Dave Cleaver
November 18, 2017
Abstraction
2
Abstraction
• Separate the what from the how
3
Abstraction
• Separate the what from the how
• Utilize simpler implementations to test
4
Techniques
• Tagless Final
• Free Monad
5
Tagless Final
6
trait OrderRepositoryAlgebra[F[_]] {
def put(order: Order): F[Order]
def get(orderId: Long): F[Option[Order]]
def delete(orderId: Long): F[Option[Order]]
}
7
trait OrderRepositoryAlgebra[F[_]] {
def put(order: Order): F[Order]
def get(orderId: Long): F[Option[Order]]
def delete(orderId: Long): F[Option[Order]]
}
8
trait OrderRepositoryAlgebra[F[_]] {
def put(order: Order): F[Order]
def get(orderId: Long): F[Option[Order]]
def delete(orderId: Long): F[Option[Order]]
}
9
Using the Algebra
10
class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) {
def placeOrder(order: Order): F[Order] = orderRepo.put(order)
def updateStatus(orderId: Long, status: OrderStatus)
(implicit M: Monad[F]): EitherT[F, OrderError, Order] =
for {
order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId))
updated = order.copy(status = status)
_ <- EitherT.right[OrderError](orderRepo.put(updated))
} yield updated
}
11
class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) {
def placeOrder(order: Order): F[Order] = orderRepo.put(order)
def updateStatus(orderId: Long, status: OrderStatus)
(implicit M: Monad[F]): EitherT[F, OrderError, Order] =
for {
order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId))
updated = order.copy(status = status)
_ <- EitherT.right[OrderError](orderRepo.put(updated))
} yield updated
}
12
class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) {
def placeOrder(order: Order): F[Order] = orderRepo.put(order)
def updateStatus(orderId: Long, status: OrderStatus)
(implicit M: Monad[F]): EitherT[F, OrderError, Order] =
for {
order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId))
updated = order.copy(status = status)
_ <- EitherT.right[OrderError](orderRepo.put(updated))
} yield updated
}
13
Production Interpreter
class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
extends OrderRepositoryAlgebra[F] {
…
def put(order: Order): F[Order] = {
val insert: ConnectionIO[Order] = for {
id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID")
} yield order.copy(id = Some(id))
insert.transact(xa)
}
def get(orderId: Long): F[Option[Order]] = …
def delete(orderId: Long): F[Option[Order]] = …
}
15
class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
extends OrderRepositoryAlgebra[F] {
…
def put(order: Order): F[Order] = {
val insert: ConnectionIO[Order] = for {
id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID")
} yield order.copy(id = Some(id))
insert.transact(xa)
}
def get(orderId: Long): F[Option[Order]] = …
def delete(orderId: Long): F[Option[Order]] = …
}
16
class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F])
extends OrderRepositoryAlgebra[F] {
…
def put(order: Order): F[Order] = {
val insert: ConnectionIO[Order] = for {
id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID")
} yield order.copy(id = Some(id))
insert.transact(xa)
}
def get(orderId: Long): F[Option[Order]] = …
def delete(orderId: Long): F[Option[Order]] = …
}
17
Test Interpreter
18
class OrderRepositoryInMemoryInterpreter[F[_]: Applicative]
extends OrderRepositoryAlgebra[F] {
private val cache = new TrieMap[Long, Order]
private val random = new Random
def put(order: Order): F[Order] = {
val toSave = if (order.id.isDefined) order
else order.copy(id = Some(random.nextLong))
toSave.id.foreach { cache.put(_, toSave) }
toSave.pure[F]
}
def get(orderId: Long): F[Option[Order]] =
cache.get(orderId).pure[F]
def delete(orderId: Long): F[Option[Order]] =
cache.remove(orderId).pure[F]
}
19
class OrderRepositoryInMemoryInterpreter[F[_]: Applicative]
extends OrderRepositoryAlgebra[F] {
private val cache = new TrieMap[Long, Order]
private val random = new Random
def put(order: Order): F[Order] = {
val toSave = if (order.id.isDefined) order
else order.copy(id = Some(random.nextLong))
toSave.id.foreach { cache.put(_, toSave) }
toSave.pure[F]
}
def get(orderId: Long): F[Option[Order]] =
cache.get(orderId).pure[F]
def delete(orderId: Long): F[Option[Order]] =
cache.remove(orderId).pure[F]
}
20
class OrderRepositoryInMemoryInterpreter[F[_]: Applicative]
extends OrderRepositoryAlgebra[F] {
private val cache = new TrieMap[Long, Order]
private val random = new Random
def put(order: Order): F[Order] = {
val toSave = if (order.id.isDefined) order
else order.copy(id = Some(random.nextLong))
toSave.id.foreach { cache.put(_, toSave) }
toSave.pure[F]
}
def get(orderId: Long): F[Option[Order]] =
cache.get(orderId).pure[F]
def delete(orderId: Long): F[Option[Order]] =
cache.remove(orderId).pure[F]
}
21
What can we do with that?
• Unit Testing
• Property-based Testing
22
Limits
• Known inputs
• Possible inputs
• Under normal conditions
23
Generating Programs
24
Generating Programs
• Derive possible outputs (Gen[Output])
25
Generating Programs
• Derive possible outputs (Gen[Output])
• From the inputs (CoGen[Input])
26
Generating Interpreter
27
class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long])
(implicit longInput: Cogen[Long])
extends OrderRepositoryAlgebra[Gen] {
def put(order: Order): Gen[Order] =
for {
id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_))
order <- Gen.const(order.copy(id = id))
} yield order
def get(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
def delete(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
}
28
class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long])
(implicit longInput: Cogen[Long])
extends OrderRepositoryAlgebra[Gen] {
def put(order: Order): Gen[Order] =
for {
id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_))
order <- Gen.const(order.copy(id = id))
} yield order
def get(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
def delete(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
}
29
class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long])
(implicit longInput: Cogen[Long])
extends OrderRepositoryAlgebra[Gen] {
def put(order: Order): Gen[Order] =
for {
id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_))
order <- Gen.const(order.copy(id = id))
} yield order
def get(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
def delete(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
}
30
class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long])
(implicit longInput: Cogen[Long])
extends OrderRepositoryAlgebra[Gen] {
def put(order: Order): Gen[Order] =
for {
id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_))
order <- Gen.const(order.copy(id = id))
} yield order
def get(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
def delete(orderId: Long): Gen[Option[Order]] =
longInput.cogen(orderId, response)
}
31
Tracing
32
sealed trait OrderRepositoryTrace
33
sealed trait OrderRepositoryTrace
case class TracePut(order: Order) extends OrderRepositoryTrace
case class TraceGet(orderId: Long) extends OrderRepositoryTrace
case class TraceDelete(orderId: Long) extends OrderRepositoryTrace
34
sealed trait OrderRepositoryTrace
case class TracePut(order: Order) extends OrderRepositoryTrace
case class TraceGet(orderId: Long) extends OrderRepositoryTrace
case class TraceDelete(orderId: Long) extends OrderRepositoryTrace
35
Tracing Interpreter
36
class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F])
(implicit M: Monad[F])
extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] {
def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] =
for {
_ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message))
result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g)
} yield result
def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] =
log(TracePut(order)) { wrapped.put(order) }
def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceGet(orderId)) { wrapped.get(orderId) }
def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceDelete(orderId)) { wrapped.delete(orderId) }
}
37
class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F])
(implicit M: Monad[F])
extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] {
def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] =
for {
_ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message))
result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g)
} yield result
def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] =
log(TracePut(order)) { wrapped.put(order) }
def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceGet(orderId)) { wrapped.get(orderId) }
def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceDelete(orderId)) { wrapped.delete(orderId) }
}
38
class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F])
(implicit M: Monad[F])
extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] {
def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] =
for {
_ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message))
result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g)
} yield result
def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] =
log(TracePut(order)) { wrapped.put(order) }
def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceGet(orderId)) { wrapped.get(orderId) }
def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceDelete(orderId)) { wrapped.delete(orderId) }
}
39
class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F])
(implicit M: Monad[F])
extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] {
def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] =
for {
_ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message))
result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g)
} yield result
def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] =
log(TracePut(order)) { wrapped.put(order) }
def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceGet(orderId)) { wrapped.get(orderId) }
def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] =
log(TraceDelete(orderId)) { wrapped.delete(orderId) }
}
40
Code Properties
41
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
42
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
43
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
44
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
45
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
46
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
47
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
48
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
49
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
50
test("never delete when updating status") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter)
val orderService = OrderService(orderRepo)
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.written)
forAll { (walk: List[OrderRepositoryTrace]) =>
assert(!walk.exists {
case TraceDelete(_) => true
case _ => false })
}
}
51
List(TraceGet(5))
List(TraceGet(5))
List(TraceGet(5), TracePut(Order(4,Some(1969-12-31T19:00:00.011-05:00),Delivered,true,None)))
List(TraceGet(5))
List(TraceGet(5), TracePut(Order(27,Some(1969-12-31T19:00:00.007-05:00),Delivered,false,None)))
List(TraceGet(5), TracePut(Order(40,None,Delivered,false,Some(35))))
List(TraceGet(5))
List(TraceGet(5))
List(TraceGet(5))
List(TraceGet(5), TracePut(Order(38,None,Delivered,false,None)))
52
Free
• Encode our operations as classes
• Wrap in the Free Monad
53
Free Operations
54
sealed trait OrderRepositoryOp[A]
case class PutOp(order: Order) extends OrderRepositoryOp[Order]
case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
55
sealed trait OrderRepositoryOp[A]
case class PutOp(order: Order) extends OrderRepositoryOp[Order]
case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
56
sealed trait OrderRepositoryOp[A]
case class PutOp(order: Order) extends OrderRepositoryOp[Order]
case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]]
57
Free Monad
58
class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F])
extends OrderRepositoryAlgebra[Free[F, ?]] {
def put(order: Order): Free[F, Order] =
Free.inject[OrderRepositoryOp, F](PutOp(order))
def get(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](GetOp(orderId))
def delete(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](DeleteOp(orderId))
}
59
class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F])
extends OrderRepositoryAlgebra[Free[F, ?]] {
def put(order: Order): Free[F, Order] =
Free.inject[OrderRepositoryOp, F](PutOp(order))
def get(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](GetOp(orderId))
def delete(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](DeleteOp(orderId))
}
60
class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F])
extends OrderRepositoryAlgebra[Free[F, ?]] {
def put(order: Order): Free[F, Order] =
Free.inject[OrderRepositoryOp, F](PutOp(order))
def get(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](GetOp(orderId))
def delete(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](DeleteOp(orderId))
}
61
class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F])
extends OrderRepositoryAlgebra[Free[F, ?]] {
def put(order: Order): Free[F, Order] =
Free.inject[OrderRepositoryOp, F](PutOp(order))
def get(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](GetOp(orderId))
def delete(orderId: Long): Free[F, Option[Order]] =
Free.inject[OrderRepositoryOp, F](DeleteOp(orderId))
}
62
Generating Interpreter
63
class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter)
extends (OrderRepositoryOp ~> Gen) {
def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match {
case PutOp(order) => genInterpreter.put(order)
case GetOp(orderId) => genInterpreter.get(orderId)
case DeleteOp(orderId) => genInterpreter.delete(orderId)
}
}
64
class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter)
extends (OrderRepositoryOp ~> Gen) {
def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match {
case PutOp(order) => genInterpreter.put(order)
case GetOp(orderId) => genInterpreter.get(orderId)
case DeleteOp(orderId) => genInterpreter.delete(orderId)
}
}
65
class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter)
extends (OrderRepositoryOp ~> Gen) {
def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match {
case PutOp(order) => genInterpreter.put(order)
case GetOp(orderId) => genInterpreter.get(orderId)
case DeleteOp(orderId) => genInterpreter.delete(orderId)
}
}
66
class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter)
extends (OrderRepositoryOp ~> Gen) {
def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match {
case PutOp(order) => genInterpreter.put(order)
case GetOp(orderId) => genInterpreter.get(orderId)
case DeleteOp(orderId) => genInterpreter.delete(orderId)
}
}
67
class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter)
extends (OrderRepositoryOp ~> Gen) {
def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match {
case PutOp(order) => genInterpreter.put(order)
case GetOp(orderId) => genInterpreter.get(orderId)
case DeleteOp(orderId) => genInterpreter.delete(orderId)
}
}
68
Tracing Interpreter
69
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
70
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
71
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
72
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
73
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
74
class Trace[F[_], G[_]: Monad](nt: F ~> G)
extends (F ~> WriterT[G, List[F[_]], ?]) {
def apply[A](f: F[A]): WriterT[G, List[F[_]], A] =
for {
_ <- WriterT.tell[G, List[F[_]]](List(f))
a <- WriterT.lift[G, List[F[_]], A](nt(f))
} yield a
}
75
Code Properties
76
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
77
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
78
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
79
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
80
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
81
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
82
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
83
test("never delete in update") {
val orderGenInterpreter =
new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long])
val orderRepo = new OrderRepositoryFree[OrderRepositoryOp]
val orderService = OrderService(orderRepo)
val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter))
implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] =
Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written)
forAll { (walk: List[OrderRepositoryOp[_]]) =>
assert(!walk.exists {
case DeleteOp(_) => true
case _ => false })
}
}
84
Analyzing Functional Programs
• Abstract
- Using Tagless Final
- or the Free Monad
• Test
- Not just inputs and outputs
- Exercise behavior from your dependencies
- Check what your code tries to do
• Take a look at
- The Scala Pet Store: https://github.com/pauljamescleary/scala-pet-store
- Example Code: https://github.com/ComcastSamples/scala-pet-store-analyzefp
• More..
- Find me on twitter and github: dscleaver
- We’re hiring: https://jobs.comcast.com/
85

Más contenido relacionado

La actualidad más candente

Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Stefano Leli
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console componentHugo Hamon
 
Slaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in RubySlaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in RubyJason Yeo Jie Shun
 
The Ring programming language version 1.10 book - Part 103 of 212
The Ring programming language version 1.10 book - Part 103 of 212The Ring programming language version 1.10 book - Part 103 of 212
The Ring programming language version 1.10 book - Part 103 of 212Mahmoud Samir Fayed
 
Bowling Game Kata by Robert C. Martin
Bowling Game Kata by Robert C. MartinBowling Game Kata by Robert C. Martin
Bowling Game Kata by Robert C. MartinLalit Kale
 
Bowling Game Kata C#
Bowling Game Kata C#Bowling Game Kata C#
Bowling Game Kata C#Dan Stewart
 
Bowling Game Kata in C# Adapted
Bowling Game Kata in C# AdaptedBowling Game Kata in C# Adapted
Bowling Game Kata in C# AdaptedMike Clement
 
The Ring programming language version 1.5 book - Part 5 of 31
The Ring programming language version 1.5 book - Part 5 of 31The Ring programming language version 1.5 book - Part 5 of 31
The Ring programming language version 1.5 book - Part 5 of 31Mahmoud Samir Fayed
 
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
Elixir  -Tolerância a Falhas para Adultos - GDG CampinasElixir  -Tolerância a Falhas para Adultos - GDG Campinas
Elixir -Tolerância a Falhas para Adultos - GDG CampinasFabio Akita
 
AI CHALLENGE ADMIN
AI CHALLENGE ADMINAI CHALLENGE ADMIN
AI CHALLENGE ADMINAnkit Gupta
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189Mahmoud Samir Fayed
 
Python 내장 함수
Python 내장 함수Python 내장 함수
Python 내장 함수용 최
 
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184Mahmoud Samir Fayed
 
Common Pitfalls Experienced in Java
Common Pitfalls Experienced in JavaCommon Pitfalls Experienced in Java
Common Pitfalls Experienced in JavaExist
 
PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!Ivan Tsyganov
 
The Ring programming language version 1.5.4 book - Part 27 of 185
The Ring programming language version 1.5.4 book - Part 27 of 185The Ring programming language version 1.5.4 book - Part 27 of 185
The Ring programming language version 1.5.4 book - Part 27 of 185Mahmoud Samir Fayed
 

La actualidad más candente (18)

Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11Codice legacy, usciamo dal pantano! @iad11
Codice legacy, usciamo dal pantano! @iad11
 
Symfony2 - extending the console component
Symfony2 - extending the console componentSymfony2 - extending the console component
Symfony2 - extending the console component
 
Slaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in RubySlaying the Dragon: Implementing a Programming Language in Ruby
Slaying the Dragon: Implementing a Programming Language in Ruby
 
The Ring programming language version 1.10 book - Part 103 of 212
The Ring programming language version 1.10 book - Part 103 of 212The Ring programming language version 1.10 book - Part 103 of 212
The Ring programming language version 1.10 book - Part 103 of 212
 
Bowling Game Kata by Robert C. Martin
Bowling Game Kata by Robert C. MartinBowling Game Kata by Robert C. Martin
Bowling Game Kata by Robert C. Martin
 
Bowling Game Kata C#
Bowling Game Kata C#Bowling Game Kata C#
Bowling Game Kata C#
 
Bowling Game Kata in C# Adapted
Bowling Game Kata in C# AdaptedBowling Game Kata in C# Adapted
Bowling Game Kata in C# Adapted
 
Next Level Testing
Next Level TestingNext Level Testing
Next Level Testing
 
The Ring programming language version 1.5 book - Part 5 of 31
The Ring programming language version 1.5 book - Part 5 of 31The Ring programming language version 1.5 book - Part 5 of 31
The Ring programming language version 1.5 book - Part 5 of 31
 
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
Elixir  -Tolerância a Falhas para Adultos - GDG CampinasElixir  -Tolerância a Falhas para Adultos - GDG Campinas
Elixir -Tolerância a Falhas para Adultos - GDG Campinas
 
AI CHALLENGE ADMIN
AI CHALLENGE ADMINAI CHALLENGE ADMIN
AI CHALLENGE ADMIN
 
Sql
SqlSql
Sql
 
The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189The Ring programming language version 1.6 book - Part 46 of 189
The Ring programming language version 1.6 book - Part 46 of 189
 
Python 내장 함수
Python 내장 함수Python 내장 함수
Python 내장 함수
 
The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184The Ring programming language version 1.5.3 book - Part 44 of 184
The Ring programming language version 1.5.3 book - Part 44 of 184
 
Common Pitfalls Experienced in Java
Common Pitfalls Experienced in JavaCommon Pitfalls Experienced in Java
Common Pitfalls Experienced in Java
 
PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!PyCon Siberia 2016. Не доверяйте тестам!
PyCon Siberia 2016. Не доверяйте тестам!
 
The Ring programming language version 1.5.4 book - Part 27 of 185
The Ring programming language version 1.5.4 book - Part 27 of 185The Ring programming language version 1.5.4 book - Part 27 of 185
The Ring programming language version 1.5.4 book - Part 27 of 185
 

Destacado

Nelson: Rigorous Deployment for a Functional World
Nelson: Rigorous Deployment for a Functional WorldNelson: Rigorous Deployment for a Functional World
Nelson: Rigorous Deployment for a Functional WorldTimothy Perrett
 
Kafka as a message queue
Kafka as a message queueKafka as a message queue
Kafka as a message queueSoftwareMill
 
Flexible Data Representation with Fixpoint Types
Flexible Data Representation with Fixpoint TypesFlexible Data Representation with Fixpoint Types
Flexible Data Representation with Fixpoint TypesDave Cleaver
 
Disorder And Tolerance In Distributed Systems At Scale
Disorder And Tolerance In Distributed Systems At ScaleDisorder And Tolerance In Distributed Systems At Scale
Disorder And Tolerance In Distributed Systems At ScaleHelena Edelson
 
Cassandra Fundamentals - C* 2.0
Cassandra Fundamentals - C* 2.0Cassandra Fundamentals - C* 2.0
Cassandra Fundamentals - C* 2.0Russell Spitzer
 
Recsys matrix-factorizations
Recsys matrix-factorizationsRecsys matrix-factorizations
Recsys matrix-factorizationsDmitriy Selivanov
 
Building a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGLBuilding a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGLLuka Jacobowitz
 
Real World Serverless
Real World ServerlessReal World Serverless
Real World ServerlessPetr Zapletal
 
Matrix Factorizations for Recommender Systems
Matrix Factorizations for Recommender SystemsMatrix Factorizations for Recommender Systems
Matrix Factorizations for Recommender SystemsDmitriy Selivanov
 
Mining Functional Patterns
Mining Functional PatternsMining Functional Patterns
Mining Functional PatternsDebasish Ghosh
 

Destacado (10)

Nelson: Rigorous Deployment for a Functional World
Nelson: Rigorous Deployment for a Functional WorldNelson: Rigorous Deployment for a Functional World
Nelson: Rigorous Deployment for a Functional World
 
Kafka as a message queue
Kafka as a message queueKafka as a message queue
Kafka as a message queue
 
Flexible Data Representation with Fixpoint Types
Flexible Data Representation with Fixpoint TypesFlexible Data Representation with Fixpoint Types
Flexible Data Representation with Fixpoint Types
 
Disorder And Tolerance In Distributed Systems At Scale
Disorder And Tolerance In Distributed Systems At ScaleDisorder And Tolerance In Distributed Systems At Scale
Disorder And Tolerance In Distributed Systems At Scale
 
Cassandra Fundamentals - C* 2.0
Cassandra Fundamentals - C* 2.0Cassandra Fundamentals - C* 2.0
Cassandra Fundamentals - C* 2.0
 
Recsys matrix-factorizations
Recsys matrix-factorizationsRecsys matrix-factorizations
Recsys matrix-factorizations
 
Building a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGLBuilding a Tagless Final DSL for WebGL
Building a Tagless Final DSL for WebGL
 
Real World Serverless
Real World ServerlessReal World Serverless
Real World Serverless
 
Matrix Factorizations for Recommender Systems
Matrix Factorizations for Recommender SystemsMatrix Factorizations for Recommender Systems
Matrix Factorizations for Recommender Systems
 
Mining Functional Patterns
Mining Functional PatternsMining Functional Patterns
Mining Functional Patterns
 

Similar a Analyzing Functional Programs

Using Scala Slick at FortyTwo
Using Scala Slick at FortyTwoUsing Scala Slick at FortyTwo
Using Scala Slick at FortyTwoEishay Smith
 
Beyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCodeBeyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCodeAijaz Ansari
 
Functions in python
Functions in pythonFunctions in python
Functions in pythonIlian Iliev
 
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDB
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDBScala Days 2011 - Rogue: A Type-Safe DSL for MongoDB
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDBjorgeortiz85
 
Project Gålbma – Actors vs Types
Project Gålbma – Actors vs TypesProject Gålbma – Actors vs Types
Project Gålbma – Actors vs TypesRoland Kuhn
 
Linq - an overview
Linq - an overviewLinq - an overview
Linq - an overviewneontapir
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and InferenceRichard Fox
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Ruslan Shevchenko
 
CS442 - Rogue: A Scala DSL for MongoDB
CS442 - Rogue: A Scala DSL for MongoDBCS442 - Rogue: A Scala DSL for MongoDB
CS442 - Rogue: A Scala DSL for MongoDBjorgeortiz85
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystifiedAlessandro Lacava
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)MongoSF
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Codestasimus
 
SCBCN17 - El camino hacia la programación declarativa
SCBCN17 - El camino hacia la programación declarativaSCBCN17 - El camino hacia la programación declarativa
SCBCN17 - El camino hacia la programación declarativaGerard Madorell
 
pandas dataframe notes.pdf
pandas dataframe notes.pdfpandas dataframe notes.pdf
pandas dataframe notes.pdfAjeshSurejan2
 
(map Clojure everyday-tasks)
(map Clojure everyday-tasks)(map Clojure everyday-tasks)
(map Clojure everyday-tasks)Jacek Laskowski
 

Similar a Analyzing Functional Programs (19)

Using Scala Slick at FortyTwo
Using Scala Slick at FortyTwoUsing Scala Slick at FortyTwo
Using Scala Slick at FortyTwo
 
Beyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCodeBeyond Breakpoints: Advanced Debugging with XCode
Beyond Breakpoints: Advanced Debugging with XCode
 
Functions in python
Functions in pythonFunctions in python
Functions in python
 
Scala best practices
Scala best practicesScala best practices
Scala best practices
 
2013 28-03-dak-why-fp
2013 28-03-dak-why-fp2013 28-03-dak-why-fp
2013 28-03-dak-why-fp
 
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDB
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDBScala Days 2011 - Rogue: A Type-Safe DSL for MongoDB
Scala Days 2011 - Rogue: A Type-Safe DSL for MongoDB
 
Project Gålbma – Actors vs Types
Project Gålbma – Actors vs TypesProject Gålbma – Actors vs Types
Project Gålbma – Actors vs Types
 
Linq - an overview
Linq - an overviewLinq - an overview
Linq - an overview
 
Generics and Inference
Generics and InferenceGenerics and Inference
Generics and Inference
 
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]Embedding Generic Monadic Transformer into Scala. [Tfp2022]
Embedding Generic Monadic Transformer into Scala. [Tfp2022]
 
CS442 - Rogue: A Scala DSL for MongoDB
CS442 - Rogue: A Scala DSL for MongoDBCS442 - Rogue: A Scala DSL for MongoDB
CS442 - Rogue: A Scala DSL for MongoDB
 
Ecma script 5
Ecma script 5Ecma script 5
Ecma script 5
 
Monads and friends demystified
Monads and friends demystifiedMonads and friends demystified
Monads and friends demystified
 
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
Map/reduce, geospatial indexing, and other cool features (Kristina Chodorow)
 
Groovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony CodeGroovy vs Boilerplate and Ceremony Code
Groovy vs Boilerplate and Ceremony Code
 
SCBCN17 - El camino hacia la programación declarativa
SCBCN17 - El camino hacia la programación declarativaSCBCN17 - El camino hacia la programación declarativa
SCBCN17 - El camino hacia la programación declarativa
 
pandas dataframe notes.pdf
pandas dataframe notes.pdfpandas dataframe notes.pdf
pandas dataframe notes.pdf
 
PythonOOP
PythonOOPPythonOOP
PythonOOP
 
(map Clojure everyday-tasks)
(map Clojure everyday-tasks)(map Clojure everyday-tasks)
(map Clojure everyday-tasks)
 

Último

Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleAlluxio, Inc.
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...asadnawaz62
 
Introduction-To-Agricultural-Surveillance-Rover.pptx
Introduction-To-Agricultural-Surveillance-Rover.pptxIntroduction-To-Agricultural-Surveillance-Rover.pptx
Introduction-To-Agricultural-Surveillance-Rover.pptxk795866
 
An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...Chandu841456
 
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdf
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdfCCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdf
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdfAsst.prof M.Gokilavani
 
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdf
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdfCCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdf
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdfAsst.prof M.Gokilavani
 
Churning of Butter, Factors affecting .
Churning of Butter, Factors affecting  .Churning of Butter, Factors affecting  .
Churning of Butter, Factors affecting .Satyam Kumar
 
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube Exchanger
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube ExchangerStudy on Air-Water & Water-Water Heat Exchange in a Finned Tube Exchanger
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube ExchangerAnamika Sarkar
 
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptxDecoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptxJoão Esperancinha
 
What are the advantages and disadvantages of membrane structures.pptx
What are the advantages and disadvantages of membrane structures.pptxWhat are the advantages and disadvantages of membrane structures.pptx
What are the advantages and disadvantages of membrane structures.pptxwendy cai
 
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsync
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsyncWhy does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsync
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsyncssuser2ae721
 
main PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidmain PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidNikhilNagaraju
 
Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024hassan khalil
 
8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitterShivangiSharma879191
 
Call Girls Delhi {Jodhpur} 9711199012 high profile service
Call Girls Delhi {Jodhpur} 9711199012 high profile serviceCall Girls Delhi {Jodhpur} 9711199012 high profile service
Call Girls Delhi {Jodhpur} 9711199012 high profile servicerehmti665
 
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)Dr SOUNDIRARAJ N
 
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort service
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort serviceGurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort service
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort servicejennyeacort
 

Último (20)

Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at Scale
 
Call Us -/9953056974- Call Girls In Vikaspuri-/- Delhi NCR
Call Us -/9953056974- Call Girls In Vikaspuri-/- Delhi NCRCall Us -/9953056974- Call Girls In Vikaspuri-/- Delhi NCR
Call Us -/9953056974- Call Girls In Vikaspuri-/- Delhi NCR
 
complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...complete construction, environmental and economics information of biomass com...
complete construction, environmental and economics information of biomass com...
 
Introduction-To-Agricultural-Surveillance-Rover.pptx
Introduction-To-Agricultural-Surveillance-Rover.pptxIntroduction-To-Agricultural-Surveillance-Rover.pptx
Introduction-To-Agricultural-Surveillance-Rover.pptx
 
An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...An experimental study in using natural admixture as an alternative for chemic...
An experimental study in using natural admixture as an alternative for chemic...
 
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdf
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdfCCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdf
CCS355 Neural Networks & Deep Learning Unit 1 PDF notes with Question bank .pdf
 
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdf
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdfCCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdf
CCS355 Neural Network & Deep Learning UNIT III notes and Question bank .pdf
 
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptxExploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
Exploring_Network_Security_with_JA3_by_Rakesh Seal.pptx
 
Churning of Butter, Factors affecting .
Churning of Butter, Factors affecting  .Churning of Butter, Factors affecting  .
Churning of Butter, Factors affecting .
 
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube Exchanger
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube ExchangerStudy on Air-Water & Water-Water Heat Exchange in a Finned Tube Exchanger
Study on Air-Water & Water-Water Heat Exchange in a Finned Tube Exchanger
 
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptxDecoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
Decoding Kotlin - Your guide to solving the mysterious in Kotlin.pptx
 
What are the advantages and disadvantages of membrane structures.pptx
What are the advantages and disadvantages of membrane structures.pptxWhat are the advantages and disadvantages of membrane structures.pptx
What are the advantages and disadvantages of membrane structures.pptx
 
Design and analysis of solar grass cutter.pdf
Design and analysis of solar grass cutter.pdfDesign and analysis of solar grass cutter.pdf
Design and analysis of solar grass cutter.pdf
 
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsync
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsyncWhy does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsync
Why does (not) Kafka need fsync: Eliminating tail latency spikes caused by fsync
 
main PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfidmain PPT.pptx of girls hostel security using rfid
main PPT.pptx of girls hostel security using rfid
 
Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024Architect Hassan Khalil Portfolio for 2024
Architect Hassan Khalil Portfolio for 2024
 
8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter8251 universal synchronous asynchronous receiver transmitter
8251 universal synchronous asynchronous receiver transmitter
 
Call Girls Delhi {Jodhpur} 9711199012 high profile service
Call Girls Delhi {Jodhpur} 9711199012 high profile serviceCall Girls Delhi {Jodhpur} 9711199012 high profile service
Call Girls Delhi {Jodhpur} 9711199012 high profile service
 
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
UNIT III ANALOG ELECTRONICS (BASIC ELECTRONICS)
 
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort service
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort serviceGurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort service
Gurgaon ✡️9711147426✨Call In girls Gurgaon Sector 51 escort service
 

Analyzing Functional Programs

  • 1. Analyzing Functional Programs Dave Cleaver November 18, 2017
  • 3. Abstraction • Separate the what from the how 3
  • 4. Abstraction • Separate the what from the how • Utilize simpler implementations to test 4
  • 7. trait OrderRepositoryAlgebra[F[_]] { def put(order: Order): F[Order] def get(orderId: Long): F[Option[Order]] def delete(orderId: Long): F[Option[Order]] } 7
  • 8. trait OrderRepositoryAlgebra[F[_]] { def put(order: Order): F[Order] def get(orderId: Long): F[Option[Order]] def delete(orderId: Long): F[Option[Order]] } 8
  • 9. trait OrderRepositoryAlgebra[F[_]] { def put(order: Order): F[Order] def get(orderId: Long): F[Option[Order]] def delete(orderId: Long): F[Option[Order]] } 9
  • 11. class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) { def placeOrder(order: Order): F[Order] = orderRepo.put(order) def updateStatus(orderId: Long, status: OrderStatus) (implicit M: Monad[F]): EitherT[F, OrderError, Order] = for { order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId)) updated = order.copy(status = status) _ <- EitherT.right[OrderError](orderRepo.put(updated)) } yield updated } 11
  • 12. class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) { def placeOrder(order: Order): F[Order] = orderRepo.put(order) def updateStatus(orderId: Long, status: OrderStatus) (implicit M: Monad[F]): EitherT[F, OrderError, Order] = for { order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId)) updated = order.copy(status = status) _ <- EitherT.right[OrderError](orderRepo.put(updated)) } yield updated } 12
  • 13. class OrderService[F[_]](orderRepo: OrderRepositoryAlgebra[F]) { def placeOrder(order: Order): F[Order] = orderRepo.put(order) def updateStatus(orderId: Long, status: OrderStatus) (implicit M: Monad[F]): EitherT[F, OrderError, Order] = for { order <- EitherT.fromOptionF(orderRepo.get(orderId), OrderNotFound(orderId)) updated = order.copy(status = status) _ <- EitherT.right[OrderError](orderRepo.put(updated)) } yield updated } 13
  • 15. class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F]) extends OrderRepositoryAlgebra[F] { … def put(order: Order): F[Order] = { val insert: ConnectionIO[Order] = for { id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID") } yield order.copy(id = Some(id)) insert.transact(xa) } def get(orderId: Long): F[Option[Order]] = … def delete(orderId: Long): F[Option[Order]] = … } 15
  • 16. class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F]) extends OrderRepositoryAlgebra[F] { … def put(order: Order): F[Order] = { val insert: ConnectionIO[Order] = for { id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID") } yield order.copy(id = Some(id)) insert.transact(xa) } def get(orderId: Long): F[Option[Order]] = … def delete(orderId: Long): F[Option[Order]] = … } 16
  • 17. class DoobieOrderRepositoryInterpreter[F[_]: Monad](val xa: Transactor[F]) extends OrderRepositoryAlgebra[F] { … def put(order: Order): F[Order] = { val insert: ConnectionIO[Order] = for { id <- sql”…”.update.withUniqueGeneratedKeys[Long]("ID") } yield order.copy(id = Some(id)) insert.transact(xa) } def get(orderId: Long): F[Option[Order]] = … def delete(orderId: Long): F[Option[Order]] = … } 17
  • 19. class OrderRepositoryInMemoryInterpreter[F[_]: Applicative] extends OrderRepositoryAlgebra[F] { private val cache = new TrieMap[Long, Order] private val random = new Random def put(order: Order): F[Order] = { val toSave = if (order.id.isDefined) order else order.copy(id = Some(random.nextLong)) toSave.id.foreach { cache.put(_, toSave) } toSave.pure[F] } def get(orderId: Long): F[Option[Order]] = cache.get(orderId).pure[F] def delete(orderId: Long): F[Option[Order]] = cache.remove(orderId).pure[F] } 19
  • 20. class OrderRepositoryInMemoryInterpreter[F[_]: Applicative] extends OrderRepositoryAlgebra[F] { private val cache = new TrieMap[Long, Order] private val random = new Random def put(order: Order): F[Order] = { val toSave = if (order.id.isDefined) order else order.copy(id = Some(random.nextLong)) toSave.id.foreach { cache.put(_, toSave) } toSave.pure[F] } def get(orderId: Long): F[Option[Order]] = cache.get(orderId).pure[F] def delete(orderId: Long): F[Option[Order]] = cache.remove(orderId).pure[F] } 20
  • 21. class OrderRepositoryInMemoryInterpreter[F[_]: Applicative] extends OrderRepositoryAlgebra[F] { private val cache = new TrieMap[Long, Order] private val random = new Random def put(order: Order): F[Order] = { val toSave = if (order.id.isDefined) order else order.copy(id = Some(random.nextLong)) toSave.id.foreach { cache.put(_, toSave) } toSave.pure[F] } def get(orderId: Long): F[Option[Order]] = cache.get(orderId).pure[F] def delete(orderId: Long): F[Option[Order]] = cache.remove(orderId).pure[F] } 21
  • 22. What can we do with that? • Unit Testing • Property-based Testing 22
  • 23. Limits • Known inputs • Possible inputs • Under normal conditions 23
  • 25. Generating Programs • Derive possible outputs (Gen[Output]) 25
  • 26. Generating Programs • Derive possible outputs (Gen[Output]) • From the inputs (CoGen[Input]) 26
  • 28. class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long]) (implicit longInput: Cogen[Long]) extends OrderRepositoryAlgebra[Gen] { def put(order: Order): Gen[Order] = for { id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_)) order <- Gen.const(order.copy(id = id)) } yield order def get(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) def delete(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) } 28
  • 29. class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long]) (implicit longInput: Cogen[Long]) extends OrderRepositoryAlgebra[Gen] { def put(order: Order): Gen[Order] = for { id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_)) order <- Gen.const(order.copy(id = id)) } yield order def get(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) def delete(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) } 29
  • 30. class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long]) (implicit longInput: Cogen[Long]) extends OrderRepositoryAlgebra[Gen] { def put(order: Order): Gen[Order] = for { id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_)) order <- Gen.const(order.copy(id = id)) } yield order def get(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) def delete(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) } 30
  • 31. class OrderRepositoryGeneratingInterpreter(response: Gen[Option[Order]], genId: Gen[Long]) (implicit longInput: Cogen[Long]) extends OrderRepositoryAlgebra[Gen] { def put(order: Order): Gen[Order] = for { id <- order.id.map(Gen.const(_)).getOrElse(genId).map(Some(_)) order <- Gen.const(order.copy(id = id)) } yield order def get(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) def delete(orderId: Long): Gen[Option[Order]] = longInput.cogen(orderId, response) } 31
  • 34. sealed trait OrderRepositoryTrace case class TracePut(order: Order) extends OrderRepositoryTrace case class TraceGet(orderId: Long) extends OrderRepositoryTrace case class TraceDelete(orderId: Long) extends OrderRepositoryTrace 34
  • 35. sealed trait OrderRepositoryTrace case class TracePut(order: Order) extends OrderRepositoryTrace case class TraceGet(orderId: Long) extends OrderRepositoryTrace case class TraceDelete(orderId: Long) extends OrderRepositoryTrace 35
  • 37. class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F]) (implicit M: Monad[F]) extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] { def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] = for { _ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message)) result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g) } yield result def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] = log(TracePut(order)) { wrapped.put(order) } def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceGet(orderId)) { wrapped.get(orderId) } def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceDelete(orderId)) { wrapped.delete(orderId) } } 37
  • 38. class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F]) (implicit M: Monad[F]) extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] { def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] = for { _ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message)) result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g) } yield result def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] = log(TracePut(order)) { wrapped.put(order) } def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceGet(orderId)) { wrapped.get(orderId) } def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceDelete(orderId)) { wrapped.delete(orderId) } } 38
  • 39. class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F]) (implicit M: Monad[F]) extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] { def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] = for { _ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message)) result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g) } yield result def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] = log(TracePut(order)) { wrapped.put(order) } def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceGet(orderId)) { wrapped.get(orderId) } def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceDelete(orderId)) { wrapped.delete(orderId) } } 39
  • 40. class OrderRepositoryTraceInterpreter[F[_]](wrapped: OrderRepositoryAlgebra[F]) (implicit M: Monad[F]) extends OrderRepositoryAlgebra[WriterT[F, List[OrderRepositoryTrace], ?]] { def log[A](message: OrderRepositoryTrace)(g: F[A]): WriterT[F, List[OrderRepositoryTrace], A] = for { _ <- WriterT.tell[F, List[OrderRepositoryTrace]](List(message)) result <- WriterT.lift[F, List[OrderRepositoryTrace], A](g) } yield result def put(order: Order): WriterT[F, List[OrderRepositoryTrace], Order] = log(TracePut(order)) { wrapped.put(order) } def get(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceGet(orderId)) { wrapped.get(orderId) } def delete(orderId: Long): WriterT[F, List[OrderRepositoryTrace], Option[Order]] = log(TraceDelete(orderId)) { wrapped.delete(orderId) } } 40
  • 42. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 42
  • 43. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 43
  • 44. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 44
  • 45. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 45
  • 46. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 46
  • 47. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 47
  • 48. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 48
  • 49. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 49
  • 50. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 50
  • 51. test("never delete when updating status") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryTraceInterpreter(orderGenInterpreter) val orderService = OrderService(orderRepo) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryTrace]] = Arbitrary(orderService.updateStatus(5, Delivered).value.written) forAll { (walk: List[OrderRepositoryTrace]) => assert(!walk.exists { case TraceDelete(_) => true case _ => false }) } } 51
  • 52. List(TraceGet(5)) List(TraceGet(5)) List(TraceGet(5), TracePut(Order(4,Some(1969-12-31T19:00:00.011-05:00),Delivered,true,None))) List(TraceGet(5)) List(TraceGet(5), TracePut(Order(27,Some(1969-12-31T19:00:00.007-05:00),Delivered,false,None))) List(TraceGet(5), TracePut(Order(40,None,Delivered,false,Some(35)))) List(TraceGet(5)) List(TraceGet(5)) List(TraceGet(5)) List(TraceGet(5), TracePut(Order(38,None,Delivered,false,None))) 52
  • 53. Free • Encode our operations as classes • Wrap in the Free Monad 53
  • 55. sealed trait OrderRepositoryOp[A] case class PutOp(order: Order) extends OrderRepositoryOp[Order] case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] 55
  • 56. sealed trait OrderRepositoryOp[A] case class PutOp(order: Order) extends OrderRepositoryOp[Order] case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] 56
  • 57. sealed trait OrderRepositoryOp[A] case class PutOp(order: Order) extends OrderRepositoryOp[Order] case class GetOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] case class DeleteOp(orderId: Long) extends OrderRepositoryOp[Option[Order]] 57
  • 59. class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F]) extends OrderRepositoryAlgebra[Free[F, ?]] { def put(order: Order): Free[F, Order] = Free.inject[OrderRepositoryOp, F](PutOp(order)) def get(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](GetOp(orderId)) def delete(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](DeleteOp(orderId)) } 59
  • 60. class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F]) extends OrderRepositoryAlgebra[Free[F, ?]] { def put(order: Order): Free[F, Order] = Free.inject[OrderRepositoryOp, F](PutOp(order)) def get(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](GetOp(orderId)) def delete(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](DeleteOp(orderId)) } 60
  • 61. class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F]) extends OrderRepositoryAlgebra[Free[F, ?]] { def put(order: Order): Free[F, Order] = Free.inject[OrderRepositoryOp, F](PutOp(order)) def get(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](GetOp(orderId)) def delete(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](DeleteOp(orderId)) } 61
  • 62. class OrderRepositoryFree[F[_]](implicit I: InjectK[OrderRepositoryOp, F]) extends OrderRepositoryAlgebra[Free[F, ?]] { def put(order: Order): Free[F, Order] = Free.inject[OrderRepositoryOp, F](PutOp(order)) def get(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](GetOp(orderId)) def delete(orderId: Long): Free[F, Option[Order]] = Free.inject[OrderRepositoryOp, F](DeleteOp(orderId)) } 62
  • 64. class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter) extends (OrderRepositoryOp ~> Gen) { def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match { case PutOp(order) => genInterpreter.put(order) case GetOp(orderId) => genInterpreter.get(orderId) case DeleteOp(orderId) => genInterpreter.delete(orderId) } } 64
  • 65. class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter) extends (OrderRepositoryOp ~> Gen) { def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match { case PutOp(order) => genInterpreter.put(order) case GetOp(orderId) => genInterpreter.get(orderId) case DeleteOp(orderId) => genInterpreter.delete(orderId) } } 65
  • 66. class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter) extends (OrderRepositoryOp ~> Gen) { def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match { case PutOp(order) => genInterpreter.put(order) case GetOp(orderId) => genInterpreter.get(orderId) case DeleteOp(orderId) => genInterpreter.delete(orderId) } } 66
  • 67. class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter) extends (OrderRepositoryOp ~> Gen) { def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match { case PutOp(order) => genInterpreter.put(order) case GetOp(orderId) => genInterpreter.get(orderId) case DeleteOp(orderId) => genInterpreter.delete(orderId) } } 67
  • 68. class OrderRepositoryOpAsGen(genInterpreter: OrderRepositoryGeneratingInterpreter) extends (OrderRepositoryOp ~> Gen) { def apply[A](in: OrderRepositoryOp[A]): Gen[A] = in match { case PutOp(order) => genInterpreter.put(order) case GetOp(orderId) => genInterpreter.get(orderId) case DeleteOp(orderId) => genInterpreter.delete(orderId) } } 68
  • 70. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 70
  • 71. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 71
  • 72. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 72
  • 73. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 73
  • 74. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 74
  • 75. class Trace[F[_], G[_]: Monad](nt: F ~> G) extends (F ~> WriterT[G, List[F[_]], ?]) { def apply[A](f: F[A]): WriterT[G, List[F[_]], A] = for { _ <- WriterT.tell[G, List[F[_]]](List(f)) a <- WriterT.lift[G, List[F[_]], A](nt(f)) } yield a } 75
  • 77. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 77
  • 78. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 78
  • 79. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 79
  • 80. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 80
  • 81. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 81
  • 82. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 82
  • 83. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 83
  • 84. test("never delete in update") { val orderGenInterpreter = new OrderRepositoryGeneratingInterpreter(Gen.option(order.arbitrary), Gen.posNum[Long]) val orderRepo = new OrderRepositoryFree[OrderRepositoryOp] val orderService = OrderService(orderRepo) val interpreter = new Trace(new OrderRepositoryOpAsGen(orderGenInterpreter)) implicit val arbitraryWalk: Arbitrary[List[OrderRepositoryOp[_]]] = Arbitrary(orderService.updateStatus(5, Delivered).value.foldMap(interpreter).written) forAll { (walk: List[OrderRepositoryOp[_]]) => assert(!walk.exists { case DeleteOp(_) => true case _ => false }) } } 84
  • 85. Analyzing Functional Programs • Abstract - Using Tagless Final - or the Free Monad • Test - Not just inputs and outputs - Exercise behavior from your dependencies - Check what your code tries to do • Take a look at - The Scala Pet Store: https://github.com/pauljamescleary/scala-pet-store - Example Code: https://github.com/ComcastSamples/scala-pet-store-analyzefp • More.. - Find me on twitter and github: dscleaver - We’re hiring: https://jobs.comcast.com/ 85