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

Autonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptAutonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptbibisarnayak0
 
DM Pillar Training Manual.ppt will be useful in deploying TPM in project
DM Pillar Training Manual.ppt will be useful in deploying TPM in projectDM Pillar Training Manual.ppt will be useful in deploying TPM in project
DM Pillar Training Manual.ppt will be useful in deploying TPM in projectssuserb6619e
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catcherssdickerson1
 
US Department of Education FAFSA Week of Action
US Department of Education FAFSA Week of ActionUS Department of Education FAFSA Week of Action
US Department of Education FAFSA Week of ActionMebane Rash
 
IVE Industry Focused Event - Defence Sector 2024
IVE Industry Focused Event - Defence Sector 2024IVE Industry Focused Event - Defence Sector 2024
IVE Industry Focused Event - Defence Sector 2024Mark Billinghurst
 
Crystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxCrystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxachiever3003
 
Engineering Drawing section of solid
Engineering Drawing     section of solidEngineering Drawing     section of solid
Engineering Drawing section of solidnamansinghjarodiya
 
Internet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxInternet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxVelmuruganTECE
 
Transport layer issues and challenges - Guide
Transport layer issues and challenges - GuideTransport layer issues and challenges - Guide
Transport layer issues and challenges - GuideGOPINATHS437943
 
Mine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxMine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxRomil Mishra
 
11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdfHafizMudaserAhmad
 
Energy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxEnergy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxsiddharthjain2303
 
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Erbil Polytechnic University
 
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgUnit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgsaravananr517913
 
home automation using Arduino by Aditya Prasad
home automation using Arduino by Aditya Prasadhome automation using Arduino by Aditya Prasad
home automation using Arduino by Aditya Prasadaditya806802
 
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
 
Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleAlluxio, Inc.
 
System Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingSystem Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingBootNeck1
 
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
 
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONTHE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONjhunlian
 

Dernier (20)

Autonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.pptAutonomous emergency braking system (aeb) ppt.ppt
Autonomous emergency braking system (aeb) ppt.ppt
 
DM Pillar Training Manual.ppt will be useful in deploying TPM in project
DM Pillar Training Manual.ppt will be useful in deploying TPM in projectDM Pillar Training Manual.ppt will be useful in deploying TPM in project
DM Pillar Training Manual.ppt will be useful in deploying TPM in project
 
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor CatchersTechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
TechTAC® CFD Report Summary: A Comparison of Two Types of Tubing Anchor Catchers
 
US Department of Education FAFSA Week of Action
US Department of Education FAFSA Week of ActionUS Department of Education FAFSA Week of Action
US Department of Education FAFSA Week of Action
 
IVE Industry Focused Event - Defence Sector 2024
IVE Industry Focused Event - Defence Sector 2024IVE Industry Focused Event - Defence Sector 2024
IVE Industry Focused Event - Defence Sector 2024
 
Crystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptxCrystal Structure analysis and detailed information pptx
Crystal Structure analysis and detailed information pptx
 
Engineering Drawing section of solid
Engineering Drawing     section of solidEngineering Drawing     section of solid
Engineering Drawing section of solid
 
Internet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptxInternet of things -Arshdeep Bahga .pptx
Internet of things -Arshdeep Bahga .pptx
 
Transport layer issues and challenges - Guide
Transport layer issues and challenges - GuideTransport layer issues and challenges - Guide
Transport layer issues and challenges - Guide
 
Mine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptxMine Environment II Lab_MI10448MI__________.pptx
Mine Environment II Lab_MI10448MI__________.pptx
 
11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf11. Properties of Liquid Fuels in Energy Engineering.pdf
11. Properties of Liquid Fuels in Energy Engineering.pdf
 
Energy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptxEnergy Awareness training ppt for manufacturing process.pptx
Energy Awareness training ppt for manufacturing process.pptx
 
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
Comparative study of High-rise Building Using ETABS,SAP200 and SAFE., SAFE an...
 
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfgUnit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
Unit7-DC_Motors nkkjnsdkfnfcdfknfdgfggfg
 
home automation using Arduino by Aditya Prasad
home automation using Arduino by Aditya Prasadhome automation using Arduino by Aditya Prasad
home automation using Arduino by Aditya Prasad
 
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
 
Correctly Loading Incremental Data at Scale
Correctly Loading Incremental Data at ScaleCorrectly Loading Incremental Data at Scale
Correctly Loading Incremental Data at Scale
 
System Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event SchedulingSystem Simulation and Modelling with types and Event Scheduling
System Simulation and Modelling with types and Event Scheduling
 
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
 
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTIONTHE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
THE SENDAI FRAMEWORK FOR DISASTER RISK REDUCTION
 

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