SlideShare une entreprise Scribd logo
1  sur  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

Contenu connexe

Tendances

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
 

Tendances (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
 

En vedette

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
 

En vedette (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
 

Similaire à 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
 

Similaire à 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)
 

Dernier

HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSHARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSRajkumarAkumalla
 
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Call Girls in Nagpur High Profile
 
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingrknatarajan
 
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls in Nagpur High Profile
 
Introduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxIntroduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxupamatechverse
 
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
 
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).pptssuser5c9d4b1
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...ranjana rawat
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSISrknatarajan
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxpurnimasatapathy1234
 
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130Suhani Kapoor
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur High Profile
 
HARMONY IN THE NATURE AND EXISTENCE - Unit-IV
HARMONY IN THE NATURE AND EXISTENCE - Unit-IVHARMONY IN THE NATURE AND EXISTENCE - Unit-IV
HARMONY IN THE NATURE AND EXISTENCE - Unit-IVRajaP95
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Serviceranjana rawat
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)Suman Mia
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )Tsuyoshi Horigome
 

Dernier (20)

HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICSHARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
HARDNESS, FRACTURE TOUGHNESS AND STRENGTH OF CERAMICS
 
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...Top Rated  Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
Top Rated Pune Call Girls Budhwar Peth ⟟ 6297143586 ⟟ Call Me For Genuine Se...
 
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and workingUNIT-V FMM.HYDRAULIC TURBINE - Construction and working
UNIT-V FMM.HYDRAULIC TURBINE - Construction and working
 
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service NashikCall Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
Call Girls Service Nashik Vaishnavi 7001305949 Independent Escort Service Nashik
 
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
 
Introduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptxIntroduction to IEEE STANDARDS and its different types.pptx
Introduction to IEEE STANDARDS and its different types.pptx
 
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
 
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
247267395-1-Symmetric-and-distributed-shared-memory-architectures-ppt (1).ppt
 
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
(PRIYA) Rajgurunagar Call Girls Just Call 7001035870 [ Cash on Delivery ] Pun...
 
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
9953056974 Call Girls In South Ex, Escorts (Delhi) NCR.pdf
 
UNIT-III FMM. DIMENSIONAL ANALYSIS
UNIT-III FMM.        DIMENSIONAL ANALYSISUNIT-III FMM.        DIMENSIONAL ANALYSIS
UNIT-III FMM. DIMENSIONAL ANALYSIS
 
Roadmap to Membership of RICS - Pathways and Routes
Roadmap to Membership of RICS - Pathways and RoutesRoadmap to Membership of RICS - Pathways and Routes
Roadmap to Membership of RICS - Pathways and Routes
 
Microscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptxMicroscopic Analysis of Ceramic Materials.pptx
Microscopic Analysis of Ceramic Materials.pptx
 
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130
VIP Call Girls Service Hitech City Hyderabad Call +91-8250192130
 
★ CALL US 9953330565 ( HOT Young Call Girls In Badarpur delhi NCR
★ CALL US 9953330565 ( HOT Young Call Girls In Badarpur delhi NCR★ CALL US 9953330565 ( HOT Young Call Girls In Badarpur delhi NCR
★ CALL US 9953330565 ( HOT Young Call Girls In Badarpur delhi NCR
 
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur EscortsCall Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
Call Girls in Nagpur Suman Call 7001035870 Meet With Nagpur Escorts
 
HARMONY IN THE NATURE AND EXISTENCE - Unit-IV
HARMONY IN THE NATURE AND EXISTENCE - Unit-IVHARMONY IN THE NATURE AND EXISTENCE - Unit-IV
HARMONY IN THE NATURE AND EXISTENCE - Unit-IV
 
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
(RIA) Call Girls Bhosari ( 7001035870 ) HI-Fi Pune Escorts Service
 
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)Software Development Life Cycle By  Team Orange (Dept. of Pharmacy)
Software Development Life Cycle By Team Orange (Dept. of Pharmacy)
 
SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )SPICE PARK APR2024 ( 6,793 SPICE Models )
SPICE PARK APR2024 ( 6,793 SPICE Models )
 

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