このページは http://www.slideshare.net/debasishg/domain-modeling-with-functions-an-algebraic-approach の内容を掲載しています。

掲載を希望されないスライド著者の方は、こちらよりご連絡下さい。

約1年前 (2015/09/22)にアップロードinテクノロジー

Talks about algebraic API design for domain models

- 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

• Class models the domain abstraction

models

• Contains both the state and the

behavior together

• State hidden within private access

State

Behavior

specifier for fear of being mutated

inadvertently

• Decision to take - what should go inside

a class ?

State

Behavior

• Decision to take - where do we put

behaviors that involve multiple classes ?

Often led to bloated service classes

Class

Wednesday, 23 September 15 - • Algebraic Data Type (ADT) models the

Lean domain

domain abstraction

models

• Contains only the defining state as

immutable values

Immutable

• No need to make things “private” since

Behavior

State

we are talking about immutable values

• Nothing but the bare essential

definitions go inside an ADT

Immutable

Behavior

State

• All behaviors are outside the ADT in

modules as functions that define the

domain behaviors

Algebraic Data Types Functions in modules

Wednesday, 23 September 15 - Rich domain

models

• We start with the class design

• Make it sufficiently “rich” by putting al

State

Behavior

related behaviors within the class, used to

cal them fine grained abstractions

• We put larger behaviors in the form of

services (aka managers) and used to cal

them coarse grained abstractions

State

Behavior

Class

Wednesday, 23 September 15 - Lean domain

• We start with the functions, the

models

behaviors of the domain

• We define function algebras using types

that don’t have any implementation yet

(we wil see examples shortly)

Immutable

Behavior

State

• Primary focus is on compositionality that

enables building larger functions out of

smal er ones

Immutable

Behavior

• Functions reside in modules which also

State

compose

• Entities are built with algebraic data

Algebraic Data Types Functions in modules

types that implement the types we used in

defining the functions

Wednesday, 23 September 15 - The Functional Lens ..

“domain API evolution through algebraic

composition”

Wednesday, 23 September 15 - x has a type

• Set of business rules

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

• Set of behaviors

• Can be composed

• Usual y closed under composition

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

• types

• type constraints

• expression in terms of other generic algebra

verifiable

• type constraints

• more constraints if you have DT

• algebraic property based testing

Wednesday, 23 September 15 - Problem Domain

Wednesday, 23 September 15 - Problem Domain

entities

Account

Bank

Customer

Trade

...

...

...

...

Wednesday, 23 September 15 - place

Problem Domain

order

behaviors

do trade

process

entities

execution

...

Account

Bank

Customer

Trade

...

...

...

Wednesday, 23 September 15 - place

Problem Domain

order

behaviors

do trade

process

market

entities

execution

regulations

...

Account

laws

tax laws

Bank

Customer

brokerage

Trade

...

...

commission

rates

...

...

Wednesday, 23 September 15 - place

Problem Domain

order

behaviors

do trade

process

market

entities

execution

regulations

...

Account

laws

tax laws

Bank

Customer

brokerage

Trade

...

...

commission

rates

...

...

Wednesday, 23 September 15 - place

Problem Domain

Solution Domain

order

behaviors

• Functions

do trade

• On Types

• Constraints

process

execution

...

Wednesday, 23 September 15 - place

Problem Domain

Solution Domain

order

behaviors

• Functions

do trade

• On Types

• Constraints

process

execution

...

Algebra

• Morphisms

• Sets

• Laws

Wednesday, 23 September 15 - place

Problem Domain

Solution Domain

order

behaviors

• Functions

do trade

• On Types

• Constraints

process

execution

...

Algebra

• Morphisms

Compose for larger abstractions

• Sets

• Laws

Wednesday, 23 September 15 - A Monoid

trait Monoid[A] {

def zero: A

def op(l: A, r: => A): A

An algebraic structure

}

having

object MonoidLaws {

• an identity element def associative[A: Equal: Monoid](a1:

A, a2: A, a3: A): Boolean = //..

• a binary associative def rightIdentity[A: Equal: Monoid](a:

operation

A) = //..

def leftIdentity[A: Equal: Monoid](a:

A) = //..

}

Wednesday, 23 September 15 - Monoid Laws

satisfies

trait Monoid[A] {

op(x, zero) == x and op(zero, x) == x def zero: A

def op(l: A, r: => A): A

An algebraic structure

}

havingsa

object MonoidLaws {

• an identity element def associative[A: Equal: Monoid](a1:

A, a2: A, a3: A): Boolean = //..

•

satisfies

op(o a

p( bi

x, na

y), r

z) y a

== s

o so

p( ci

x, o ati

p(y,v

e

z))

def rightIdentity[A: Equal: Monoid](a:

operation

A) = //..

def leftIdentity[A: Equal: Monoid](a:

A) = //..

}

Wednesday, 23 September 15 - A Monoid

• generic

trait Monoid[A] {

• domain independent

def zero: A

• context unaware

def op(l: A, r: => A): A

}

Wednesday, 23 September 15 - A Monoid

• generic

trait Monoid[A] {

• domain independent

def zero: A

• context unaware

def op(l: A, r: => A): A

}

implicit def MoneyPlusMonoid = new Monoid[Money] {

• context of the domain def zero = //..

def op(m1: Money, m2: Money) = //..

}

Wednesday, 23 September 15 - A Monoid

(algebra)

• generic

trait Monoid[A] {

• domain independent

def zero: A

• context unaware

def op(l: A, r: => A): A

}

implicit def MoneyPlusMonoid = new Monoid[Money] {

• context of the domain def zero = //..

def op(m1: Money, m2: Money) = //..

}

(interpretation)

Wednesday, 23 September 15 - A Monoid

(algebra)

• generic

trait Monoid[A] {

(reusable across contexts)

• domain independent

def zero: A

• context unaware

def op(l: A, r: => A): A

}

implicit def MoneyPlusMonoid = new Monoid[Money] {

• context of the domain def zero = //..

def op(m1: Money, m2: Money) = //..

}

(interpretation)

(varies with context)

Wednesday, 23 September 15 - place

order

do trade

process

execution

...

Domain

Behaviors

Wednesday, 23 September 15 - place

order

Account

Bank

Customer

do trade

Trade

...

...

...

process

execution

...

Domain

Behaviors

Domain Types

Wednesday, 23 September 15 - place

order

Account

market

Bank

regulations

Customer

do trade

Trade

tax laws

...

...

brokerage

...

process

execution

commission

rates

...

...

Domain

Behaviors

Domain Rules

Domain Types

Wednesday, 23 September 15 - place

order

Account

market

Bank

regulations

Customer

do trade

Trade

tax laws

...

...

brokerage

...

process

execution

commission

rates

...

...

Domain

Behaviors

Domain Rules

Domain Types

...

Monoid

Monad

Generic Algebraic

Structures

Wednesday, 23 September 15 - place

order

Account

market

Bank

regulations

Customer

do trade

Trade

tax laws

...

...

brokerage

...

process

execution

commission

rates

...

...

Domain Algebra

Domain

Behaviors

Domain Rules

Domain Types

...

Monoid

Monad

Generic Algebraic

Structures

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

1

2

Transform to internal domain

model entity and place for execution

Wednesday, 23 September 15 - Client places order

- flexible format

1

2

Transform to internal domain

model entity and place for execution

3

Trade & Allocate to

client accounts

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 - Types out of thin air

No implementation til now

def clientOrders: ClientOrderSheet => List[Order]

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

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

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]

Algebra of the API

• Types (domain entities)

• Functions operating on types (domain behaviors)

• Laws (business rules)

Wednesday, 23 September 15 - module

parameterized on types

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]) = ???

}

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 - let’s do some algebra ..

def clientOrders: ClientOrderSheet => List[Order]

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

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

Wednesday, 23 September 15 - let’s do some algebra ..

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 - let’s do some algebra ..

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 - let’s do some algebra ..

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: 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: ClientOrderSheet => List[Order]

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

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

Wednesday, 23 September 15- .. a problem of composition ..

def f: A => List[B]

def g: B => List[C]

def h: C => List[D]

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 - .. a problem of composition with

effects that can be generalized ..

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

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

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

Define a mapping

M[B] => B

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

Define a mapping

M[B] => B

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

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

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

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

M[M[C]]

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

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

M[C]

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

Wednesday, 23 September 15 - .. a problem of composition with

effects that can be generalized ..

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

andThen

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

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 - .. function composition with

Effects ..

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

}

It’s a Kleisli !

Wednesday, 23 September 15 - .. function composition with

Effects ..

def clientOrders: ClientOrderSheet => List[Order]

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

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

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

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

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

Follow the types

Wednesday, 23 September 15 - .. function composition with

Effects ..

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

Wednesday, 23 September 15 - .. function composition with

Effects ..

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

Wednesday, 23 September 15 - .. the complete trade generation

logic ..

def tradeGeneration(

market: Market,

broker: Account,

clientAccounts: List[Account]) = {

clientOrders andThen

execute(market, broker) andThen

allocate(clientAccounts)

}

Implementation follows the specification

Wednesday, 23 September 15 - .. the complete trade generation

logic ..

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 :-)

Wednesday, 23 September 15 - algebraic & functional

• Just Pure Functions. Lower cognitive load -

don’t have to think of the classes & data

members where behaviors wil 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 - .. function composition with

Effects ..

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

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 - .. stacking of effects ..

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

M[List[_]]

Wednesday, 23 September 15 - .. stacking of effects ..

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

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

Wednesday, 23 September 15 - Monad Transformers

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

val count: Response[Int] = some(10).right

for {

maybeCount <- count

} yield {

for {

c <- maybeCount

// use c

} yield c

}

Wednesday, 23 September 15 - Monad Transformers

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

val count: Response[Int] = some(10).right

for {

maybeCount <- count

} yield {

for {

c <- maybeCount

// use c

} yield c

}

type Error[A] = String \/ A

type Response[A] = OptionT[Error, A]

val count: Response[Int] = 10.point[Response]

for{

c <- count

// use c : c is an Int here

} yield (())

Wednesday, 23 September 15 - Monad Transformers

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

val count: Response[Int] = some(10).right

for {

maybeCount <- count

} yield {

richer algebra

for {

c <- maybeCount

// use c

} yield c

}

type Error[A] = String \/ A

type Response[A] = OptionT[Error, A]

val count: Response[Int] = 10.point[Response]

for{

c <- count

// use c : c is an Int here

} yield (())

Wednesday, 23 September 15 - Monad Transformers

• col apses the stack and gives us a single

monad to deal with

• order of stacking is important though

Wednesday, 23 September 15 - .. stacking of effects ..

def clientOrders: Kleisli[List, ClientOrderSheet, Order]

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

Wednesday, 23 September 15 - Wednesday, 23 September 15
- type StringOr[A] = String \/ A

type Valid[A] = ListT[StringOr, A]

Wednesday, 23 September 15 - type StringOr[A] = String \/ A

type 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 \/ A

type 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 - def execute(market: Market, brokerAccount: Account) =

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

listT[StringOr](

order.items.map { item =>

makeExecution(brokerAccount, market, ..)

}.sequenceU

)

}

private def makeExecution(brokerAccount: Account,

item: LineItem, market: Market): String \/ Execution = //..

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]

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]

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

def tradeGeneration(market: Market, broker: Account,

clientAccounts: List[Account]) = {

clientOrders andThen

execute(market, broker) andThen

allocate(clientAccounts)

}

composition

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

• equal y 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

implementation

Wednesday, 23 September 15 - Repository

• store domain objects

• query domain objects

• single point of interface of the domain

model

Wednesday, 23 September 15 - algebra of the repository ..

• pure

• compositional

• implementation independent

continuation hole

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]

Wednesday, 23 September 15 - define a functor for the algebra ..

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

}

}

}

Wednesday, 23 September 15 - define a functor for the algebra ..

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

}

}

}

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

you get a free monad ..

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 - build larger abstractions

monadically ..

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

for {

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

a <- query(no)

} yield a

.. and you get back a free monad

Wednesday, 23 September 15 - build larger abstractions

monadically ..

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

for {

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

a <- query(no)

} yield a

.. and you get back a free monad

.. with 2 operations chained in sequence

Wednesday, 23 September 15 - build larger abstractions

monadically ..

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

for {

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

a <- query(no)

} yield a

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

col ection 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 fal s 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, referential y 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

Wednesday, 23 September 15 the concept of parametricity and is one of the

fundamental building blocks of compositionality in FP. - Use code “mlghosh2” for a 50% discount

Wednesday, 23 September 15 - Thank You!

Wednesday, 23 September 15