domain modeling with functions - an algebraic approach

117
Domain Modeling with Functions an algebraic approach Debasish Ghosh (@debasishg) Wednesday, 23 September 15

Upload: debasish-ghosh

Post on 15-Apr-2017

5.527 views

Category:

Software


0 download

TRANSCRIPT

Domain Modeling with Functions

an algebraic approach

Debasish Ghosh(@debasishg)

Wednesday, 23 September 15

What is a domain model ?

A domain model in problem solving and software engineering is a conceptual model of all the topics related to a specific problem. It describes the various entities, their attributes, roles, and relationships, plus the constraints that govern the problem domain. It does not describe the solutions to the problem.

Wikipedia (http://en.wikipedia.org/wiki/Domain_model)

Wednesday, 23 September 15

Rich domain models

State Behavior

Class

• Class models the domain abstraction

• Contains both the state and the behavior together

• State hidden within private access specifier for fear of being mutated inadvertently

• Decision to take - what should go inside a class ?

• Decision to take - where do we put behaviors that involve multiple classes ? Often led to bloated service classes

State Behavior

Wednesday, 23 September 15

• Algebraic Data Type (ADT) models the domain abstraction

• Contains only the defining state as immutable values

• No need to make things “private” since we are talking about immutable values

• Nothing but the bare essential definitions go inside an ADT

• All behaviors are outside the ADT in modules as functions that define the domain behaviors

Lean domain models

Immutable State

Behavior

Immutable State

Behavior

Algebraic Data Types Functions in modules

Wednesday, 23 September 15

Rich domain models

State Behavior

Class

• We start with the class design

• Make it sufficiently “rich” by putting all related behaviors within the class, used to call them fine grained abstractions

• We put larger behaviors in the form of services (aka managers) and used to call them coarse grained abstractions

State Behavior

Wednesday, 23 September 15

Lean domain models

Immutable State

Behavior

• We start with the functions, the behaviors of the domain

• We define function algebras using types that don’t have any implementation yet (we will see examples shortly)

• Primary focus is on compositionality that enables building larger functions out of smaller ones

• Functions reside in modules which also compose

• Entities are built with algebraic data types that implement the types we used in defining the functions

Immutable State

Behavior

Algebraic Data Types Functions in modules

Wednesday, 23 September 15

The Functional Lens ..

“domain API evolution through algebraic composition”

Wednesday, 23 September 15

• Set of behaviors• Can be composed• Usually closed under composition

• Set of business rules

Domain Model = { f(x) | P(x) Є { domain rules }}

x has a type

Wednesday, 23 September 15

Domain Model Algebra

Wednesday, 23 September 15

Domain Model Algebra

(algebra of types, functions & laws)

Wednesday, 23 September 15

Domain Model Algebra

(algebra of types, functions & laws)

explicit• types• type constraints• expression in terms of other generic algebra

Wednesday, 23 September 15

Domain Model Algebra

(algebra of types, functions & laws)

explicit

verifiable

• types• type constraints• expression in terms of other generic algebra

• type constraints• more constraints if you have DT• algebraic property based testing

Wednesday, 23 September 15

Problem Domain

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...

Problem Domain

...

entities

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...

do trade

process execution

place order

Problem Domain

...

entities

behaviors

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...

do trade

process execution

place order

Problem Domain

...

market regulations

tax laws

brokerage commission

rates

...

entities

behaviors

laws

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...

do trade

process execution

place order

Problem Domain

...

market regulations

tax laws

brokerage commission

rates

...

entities

behaviors

laws

Wednesday, 23 September 15

do trade

process execution

place orderProblem Domain

...

behaviors • Functions• On Types• Constraints

Solution Domain

Wednesday, 23 September 15

do trade

process execution

place orderProblem Domain

...

behaviors • Functions• On Types• Constraints

Solution Domain

• Morphisms• Sets• Laws

Algebra

Wednesday, 23 September 15

do trade

process execution

place orderProblem Domain

...

behaviors • Functions• On Types• Constraints

Solution Domain

• Morphisms• Sets• Laws

Algebra

Compose for larger abstractions

Wednesday, 23 September 15

A Monoid

An algebraic structure having

• an identity element

• a binary associative operation

trait Monoid[A] { def zero: A def op(l: A, r: => A): A}

object MonoidLaws { def associative[A: Equal: Monoid](a1: A, a2: A, a3: A): Boolean = //..

def rightIdentity[A: Equal: Monoid](a: A) = //..

def leftIdentity[A: Equal: Monoid](a: A) = //..}

Wednesday, 23 September 15

Monoid Laws

An algebraic structure havingsa

• an identity element

• a binary associative operation

satisfies op(x, zero) == x and op(zero, x) == x

satisfies op(op(x, y), z) == op(x, op(y, z))

trait Monoid[A] { def zero: A def op(l: A, r: => A): A}

object MonoidLaws { def associative[A: Equal: Monoid](a1: A, a2: A, a3: A): Boolean = //..

def rightIdentity[A: Equal: Monoid](a: A) = //..

def leftIdentity[A: Equal: Monoid](a: A) = //..}

Wednesday, 23 September 15

A Monoid• generic• domain independent• context unaware

trait Monoid[A] { def zero: A def op(l: A, r: => A): A}

Wednesday, 23 September 15

A Monoidtrait Monoid[A] { def zero: A def op(l: A, r: => A): A}

• generic• domain independent• context unaware

implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //..}

• context of the domain

Wednesday, 23 September 15

A Monoidtrait Monoid[A] { def zero: A def op(l: A, r: => A): A}

• generic• domain independent• context unaware

implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //..}

• context of the domain

(algebra)

(interpretation)

Wednesday, 23 September 15

A Monoidtrait Monoid[A] { def zero: A def op(l: A, r: => A): A}

• generic• domain independent• context unaware

implicit def MoneyPlusMonoid = new Monoid[Money] { def zero = //.. def op(m1: Money, m2: Money) = //..}

• context of the domain

(algebra)

(interpretation)

(reusable across contexts)

(varies with context)

Wednesday, 23 September 15

do trade

process execution

place order

...

Domain Behaviors

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...do trade

process execution

place order

...

Domain Behaviors Domain Types

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...do trade

process execution

place order

...

market regulations

tax laws

brokerage commission

rates

...

Domain Behaviors Domain TypesDomain Rules

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...do trade

process execution

place order

...

market regulations

tax laws

brokerage commission

rates

...

Domain Behaviors Domain TypesDomain Rules

Monoid Monad ...

Generic Algebraic Structures

Wednesday, 23 September 15

Bank

Account

Trade

Customer

......

...do trade

process execution

place order

...

market regulations

tax laws

brokerage commission

rates

...

Domain Behaviors Domain TypesDomain Rules

Monoid Monad ...

Generic Algebraic Structures

Domain Algebra

Wednesday, 23 September 15

.. so we talk about domain algebra, where the domain entities are implemented with sets of types and domain behaviors are functions that

map a type to one or more types. And domain rules are the laws which define the

constraints of the business ..

Wednesday, 23 September 15

Functional Modeling encourages Algebraic API Design which leads to organic evolution of domain models

Wednesday, 23 September 15

Wednesday, 23 September 15

Client places order- flexible format

1

Wednesday, 23 September 15

Client places order- flexible format

Transform to internal domainmodel entity and place for execution

1 2

Wednesday, 23 September 15

Client places order- flexible format

Transform to internal domainmodel entity and place for execution

Trade & Allocate toclient accounts

1 2

3

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute[Account <: BrokerAccount]: Market => Account => Order => List[Execution]

def allocate[Account <: TradingAccount]: List[Account] => Execution => List[Trade]

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

Types out of thin air No implementation till now

Type names resonate domain language

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

• Types (domain entities)• Functions operating on types (domain behaviors)• Laws (business rules)

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

• Types (domain entities)• Functions operating on types (domain behaviors)• Laws (business rules)

Algebra of the API

Wednesday, 23 September 15

trait Trading[Account, Trade, ClientOrderSheet, Order, Execution, Market] {

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

def tradeGeneration(market: Market, broker: Account, clientAccounts: List[Account]) = ???}

parameterized on typesmodule

Wednesday, 23 September 15

Algebraic Design

• The algebra is the binding contract of the API

• Implementation is NOT part of the algebra

• An algebra can have multiple interpreters (aka implementations)

• One of the core principles of functional programming is to decouple the algebra from the interpreter

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute: Market => Account => Order => List[Execution]

def allocate: List[Account] => Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

let’s do some algebra ..

Wednesday, 23 September 15

def f: A => List[B]

def g: B => List[C]

def h: C => List[D]

.. a problem of composition ..

Wednesday, 23 September 15

.. a problem of composition with effects ..

def f: A => List[B]

def g: B => List[C]

def h: C => List[D]

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

def h[M: Monad]: C => M[D]

.. a problem of composition with effects that can be generalized ..

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

Define a mapping M[B] => B

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

Define a mapping M[B] => B

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

m.map(f(a))(g)

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

m.map(f(a))(g) M[M[C]]

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

m.join(m.map(f(a))(g)) M[C]

Wednesday, 23 September 15

def f[M: Monad]: A => M[B]

def g[M: Monad]: B => M[C]

.. a problem of composition with effects that can be generalized ..

andThen

Wednesday, 23 September 15

.. the glue (combinator) ..

def andThen[M[_], A, B, C](f: A => M[B], g: B => M[C])

(implicit m: Monad[M]): A => M[C] = {(a: A) =>

m.join(m.map(f(a))(g))}

Wednesday, 23 September 15

case class Kleisli[M[_], A, B](run: A => M[B]) {

def andThen[C](f: B => M[C])

(implicit M: Monad[M]): Kleisli[M, A, C] =

Kleisli((a: A) => M.flatMap(run(a))(f))}

.. function composition with Effects ..

It’s a Kleisli !

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

Follow the types

.. function composition with Effects ..

def clientOrders: ClientOrderSheet => List[Order]

def execute(m: Market, broker: Account): Order => List[Execution]

def allocate(accounts: List[Account]): Execution => List[Trade]

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

Domain algebra composed with the categorical algebra of a Kleisli Arrow

.. function composition with Effects ..

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

.. that implements the semantics of our domain algebraically ..

.. function composition with Effects ..

Wednesday, 23 September 15

def tradeGeneration( market: Market, broker: Account, clientAccounts: List[Account]) = {

clientOrders andThen execute(market, broker) andThen allocate(clientAccounts)

}

Implementation follows the specification

.. the complete trade generation logic ..

Wednesday, 23 September 15

def tradeGeneration( market: Market, broker: Account, clientAccounts: List[Account]) = {

clientOrders andThen execute(market, broker) andThen allocate(clientAccounts)

}Implementation follows the specification and we get the Ubiquitous Language for

free :-)

.. the complete trade generation logic ..

Wednesday, 23 September 15

algebraic & functional

• Just Pure Functions. Lower cognitive load - don’t have to think of the classes & data members where behaviors will reside

• Compositional. Algebras compose - we defined the algebras of our domain APIs in terms of existing, time tested algebras of Kleislis and Monads

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

.. our algebra still doesn’t handle errors that may occur within our domain

behaviors ..

.. function composition with Effects ..

Wednesday, 23 September 15

more algebra, more types

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

return type constructor

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

return type constructor

What happens in case the operation fails ?

Wednesday, 23 September 15

Error handling as an Effect

• pure and functional

• with an explicit and published algebra

• stackable with existing effects

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

.. stacking of effects ..

M[List[_]]

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

.. stacking of effects ..

M[List[_]]: M is a Monad

Wednesday, 23 September 15

type Response[A] = String \/ Option[A]

val count: Response[Int] = some(10).rightfor { maybeCount <- count} yield { for { c <- maybeCount // use c } yield c}

Monad Transformers

Wednesday, 23 September 15

type Response[A] = String \/ Option[A]

val count: Response[Int] = some(10).rightfor { maybeCount <- count} yield { for { c <- maybeCount // use c } yield c}

type Error[A] = String \/ Atype Response[A] = OptionT[Error, A]

val count: Response[Int] = 10.point[Response]for{ c <- count // use c : c is an Int here} yield (())

Monad Transformers

Wednesday, 23 September 15

type Response[A] = String \/ Option[A]

val count: Response[Int] = some(10).rightfor { maybeCount <- count} yield { for { c <- maybeCount // use c } yield c}

type Error[A] = String \/ Atype Response[A] = OptionT[Error, A]

val count: Response[Int] = 10.point[Response]for{ c <- count // use c : c is an Int here} yield (())

Monad Transformers

richer algebra

Wednesday, 23 September 15

Monad Transformers

• collapses the stack and gives us a single monad to deal with

• order of stacking is important though

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

.. stacking of effects ..

case class ListT[M[_], A] (run: M[List[A]]) { //..

Wednesday, 23 September 15

Wednesday, 23 September 15

type StringOr[A] = String \/ Atype Valid[A] = ListT[StringOr, A]

Wednesday, 23 September 15

type StringOr[A] = String \/ Atype Valid[A] = ListT[StringOr, A]

def clientOrders: Kleisli[Valid, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[Valid, Order, Execution]

def allocate(acts: List[Account]): Kleisli[Valid, Execution, Trade]

Wednesday, 23 September 15

type StringOr[A] = String \/ Atype Valid[A] = ListT[StringOr, A]

def clientOrders: Kleisli[Valid, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[Valid, Order, Execution]

def allocate(acts: List[Account]): Kleisli[Valid, Execution, Trade]

.. a small change in algebra, a huge step for our domain model ..

Wednesday, 23 September 15

def execute(market: Market, brokerAccount: Account) =

kleisli[List, Order, Execution] { order =>

order.items.map { item => Execution(brokerAccount, market, ..) }

}

Wednesday, 23 September 15

private def makeExecution(brokerAccount: Account, item: LineItem, market: Market): String \/ Execution = //..

def execute(market: Market, brokerAccount: Account) =

kleisli[Valid, Order, Execution] { order =>

listT[StringOr](

order.items.map { item =>

makeExecution(brokerAccount, market, ..)

}.sequenceU

) }

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

.. the algebra ..

Wednesday, 23 September 15

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

.. the algebra ..

functions

Wednesday, 23 September 15

.. the algebra ..

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

def execute(m: Market, b: Account): Kleisli[List, Order, Execution]

def allocate(acts: List[Account]): Kleisli[List, Execution, Trade]

types

Wednesday, 23 September 15

.. the algebra ..

composition

def tradeGeneration(market: Market, broker: Account, clientAccounts: List[Account]) = {

clientOrders andThen execute(market, broker) andThen allocate(clientAccounts)}

Wednesday, 23 September 15

.. the algebra ..

trait OrderLaw {

def sizeLaw: Seq[ClientOrder] => Seq[Order] => Boolean = { cos => orders => cos.size == orders.size }

def lineItemLaw: Seq[ClientOrder] => Seq[Order] => Boolean = { cos => orders => cos.map(instrumentsInClientOrder).sum == orders.map(_.items.size).sum }}

laws of the algebra (domain rules)

Wednesday, 23 September 15

Domain Rules as Algebraic Properties

• part of the abstraction

• equally important as the actual abstraction

• verifiable as properties

Wednesday, 23 September 15

.. domain rules verification ..

property("Check Client Order laws") =

forAll((cos: Set[ClientOrder]) => {

val orders = for { os <- clientOrders.run(cos.toList) } yield os

sizeLaw(cos.toSeq)(orders) == true

lineItemLaw(cos.toSeq)(orders) == true

})

property based testing FTW ..

Wednesday, 23 September 15

more algebra, more types

Wednesday, 23 September 15

a useful pattern for decoupling algebra from

implementationWednesday, 23 September 15

Repository

• store domain objects

• query domain objects

• single point of interface of the domain model

Wednesday, 23 September 15

sealed trait AccountRepoF[+A]

case class Query[+A](no: String, onResult: Account => A) extends AccountRepoF[A]

case class Store[+A](account: Account, next: A) extends AccountRepoF[A]

case class Delete[+A](no: String, next: A) extends AccountRepoF[A]

algebra of the repository ..

• pure• compositional• implementation independent

continuation hole

Wednesday, 23 September 15

object AccountRepoF { implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {

def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] =

action match { case Store(account, next) => Store(account, f(next)) case Query(no, onResult) => Query(no, onResult andThen f) case Delete(no, next) => Delete(no, f(next)) }

}}

define a functor for the algebra ..

Wednesday, 23 September 15

object AccountRepoF { implicit val functor: Functor[AccountRepoF] = new Functor[AccountRepoF] {

def map[A,B](action: AccountRepoF[A])(f: A => B): AccountRepoF[B] =

action match { case Store(account, next) => Store(account, f(next)) case Query(no, onResult) => Query(no, onResult andThen f) case Delete(no, next) => Delete(no, f(next)) }

}}

define a functor for the algebra ..

you get a free monad ..

type AccountRepo[A] = Free[AccountRepoF, A]

Wednesday, 23 September 15

lift your algebra into the context of the free monad ..

trait AccountRepository {

def store(account: Account): AccountRepo[Unit] = liftF(Store(account, ()))

def query(no: String): AccountRepo[Account] = liftF(Query(no, identity))

def delete(no: String): AccountRepo[Unit] = liftF(Delete(no, ()))

}

Wednesday, 23 September 15

lift your algebra into the context of the free monad ..

trait AccountRepository {

def store(account: Account): AccountRepo[Unit] = liftF(Store(account, ()))

def query(no: String): AccountRepo[Account] = liftF(Query(no, identity))

def delete(no: String): AccountRepo[Unit] = liftF(Delete(no, ()))

def update(no: String, f: Account => Account): AccountRepo[Unit] = for { a <- query(no) _ <- store(f(a)) } yield ()

}

Wednesday, 23 September 15

def open(no: String, name: String, openingDate: Option[Date]) = for {

_ <- store(Account(no, name, openingDate.get))

a <- query(no)

} yield a

build larger abstractions monadically ..

.. and you get back a free monad

Wednesday, 23 September 15

def open(no: String, name: String, openingDate: Option[Date]) = for {

_ <- store(Account(no, name, openingDate.get))

a <- query(no)

} yield a

build larger abstractions monadically ..

.. and you get back a free monad.. with 2 operations chained in sequence

Wednesday, 23 September 15

def open(no: String, name: String, openingDate: Option[Date]) = for {

_ <- store(Account(no, name, openingDate.get))

a <- query(no)

} yield a

build larger abstractions monadically ..

.. and you get back a free monad.. with 2 operations chained in sequence

.. just the algebra, no semantics

Wednesday, 23 September 15

Essence of the Pattern

• We have built the entire model of computation without any semantics, just the algebra

• Just a description of what we intend to do

• Not surprising that it’s a pure abstraction

Wednesday, 23 September 15

Essence of the Pattern

• Now we can provide as many interpreters as we wish depending on the usage / context

• 1 interpreter for testing that tests the repository actions against an in-memory data structure

• 1 interpreter for production that uses an RDBMS

Wednesday, 23 September 15

a sample interpreter structure ..

def interpret[A](script: AccountRepo[A], ls: List[String]): List[String] = script.fold(_ => ls, {

case Query(no, onResult) => interpret(..)

case Store(account, next) => interpret(..)

case Delete(no, next) => interpret(..)

})

Interpret the whole abstraction and provide the implementation in context

Wednesday, 23 September 15

Intuition ..

• The larger algebra formed from each individual algebra element is merely a collection without any interpretation, something like an AST

• The interpreter provides the context and the implementation of each of the algebra elements under that specific context

Wednesday, 23 September 15

Takeaways ..

Wednesday, 23 September 15

algebraic design

• evolution based on contracts / types / interfaces without any dependency on implementation

Wednesday, 23 September 15

algebraic design

• evolves straight from the domain use cases using domain vocabulary (ubiquitous language falls in place because of this direct correspondence)

Wednesday, 23 September 15

algebraic design

• modular and hence pluggable. Each of the API that we discussed can be plugged off the specific use case and independently used in other use cases

Wednesday, 23 September 15

algebraic design

• pure, referentially transparent and hence testable in isolation

Wednesday, 23 September 15

algebraic design

• compositional, composes with the domain algebra and with the other categorical algebras inheriting their semantics seamlessly into the domain model e.g. effectful composition with kleislis and fail-fast error handling with monads

Wednesday, 23 September 15

When using functional modeling, always try to express domain specific abstractions and behaviors in terms of more generic, lawful abstractions. Doing this you make your functions more generic, more usable in a broader context and yet simpler to comprehend.

This is the concept of parametricity and is one of the fundamental building blocks of compositionality in FP.

Wednesday, 23 September 15

Use code “mlghosh2” for a 50% discount

Wednesday, 23 September 15

Thank You!

Wednesday, 23 September 15