SlideShare uma empresa Scribd logo
1 de 67
Baixar para ler offline
Algebraic Thinking for
Evolution of Pure
Functional Domain Models
Debasish Ghosh
@debasishg
Traveling back in time ..
more than 40 years ..
Copyright: https://www.npr.org/2014/06/17/318605598/turn-the-clock-back-or-forward-with-time-traveling-tales
c !:= 0
for i !:= 1 step 1 until n do
c !:= c + a[i] x b[i]
Von Neumann program for Inner Product
“It is dynamic and repetitive.
One must mentally execute it to understand it”
- John Backus
Def Innerproduct =
(Insert +) o (ApplyToAll X) o Transpose
Composition (o), Insert, ApplyToAll etc. are
functional forms that combine existing functions to
form new ones
Functional program for Inner Product
“It’s structure is helpful in understanding it
without mentally executing it”
- John Backus
- John Backus
“.. programs can be expressed in a language that has
an associated algebra. This algebra can be used to
transform programs and to solve some equations
whose " unknowns" are programs, in much the same
way one solves equations in high school algebra.
Algebraic transformations and proofs use the
language of the programs themselves, rather than the
language of logic, which talks about programs.”
What is an Algebra ?
Algebra is the study of algebraic structures
In mathematics, and more specifically in abstract
algebra, an algebraic structure is
a set (called carrier set or underlying set)
with one or more finitary operations defined on it
that satisfies a list of axioms
-Wikipedia
(https://en.wikipedia.org/wiki/Algebraic_structure)
SetA
ϕ : A × A → A
for (a, b) ∈ A
ϕ(a, b)
a ϕ b
given
a binary operation
for specific a, b
or
The Algebra of Sets
Algebraic Thinking
• Denotational Semantics
✦ programs and the objects they manipulate are symbolic
realizations of abstract mathematical objects
✦ the purpose of a mathematical semantics is to give a
correct and meaningful correspondence between programs
and mathematical entities in a way that is entirely
independent of an implementation [Scott & Strachey,
1971]
Algebraic Thinking
Denotational Semantics
Operational Thinking
• Operational Semantics
✦ formalize program implementation and how the various
functions must be computed or represented
✦ not much of a relevance towards algebraic reasoning
Option.apply[A](a: A): Option[A]
Option.empty[A]: Option[A]
Option[A]
A: Carrier Type of the algebra
Introduction Forms
Option.apply[A](a: A): Option[A]
Option.empty[A]: Option[A]
def f[A, B](func: A !=> B) = ???
optionA.map(f)
!// Option[B]
def f[A, B](func: A !=> Option[B]) = ???
optionA.!flatMap(f)
!// Option[B]
Option[A]
A: Carrier Type of the algebra
Introduction Forms
Combinators
Option.apply[A](a: A): Option[A]
Option.empty[A]: Option[A]
def f[A, B](func: A !=> B) = ???
optionA.map(f)
!// Option[B]
def f[A, B](func: A !=> Option[B]) = ???
optionA.!flatMap(f)
!// Option[B]
optionA.getOrElse(default)
!// A or B !>: A
Option[A]
A: Carrier Type of the algebra
Introduction Forms
Combinators
Eliminator Forms
Option.apply[A](a: A): Option[A]
Option.empty[A]: Option[A]
def f[A, B](func: A !=> B) = ???
optionA.map(f)
!// Option[B]
def f[A, B](func: A !=> Option[B]) = ???
optionA.!flatMap(f)
!// Option[B]
optionA.getOrElse(default)
!// A or B !>: A
Option.empty[Int].!flatMap(!!...) !== Option.empty[Int]
!// res1: Boolean = true
Option.empty[Int].map(!!...) !== Option.empty[Int]
!// res2: Boolean = true
Option[A]
A: Carrier Type of the algebra
Introduction Forms
Combinators
Eliminator Forms
Laws
A: Carrier Type of the algebra
Introduction Forms
Combinators
Eliminator Forms
Laws
algebra
• Thinking in terms of combinators (map/
!flatMap/fold) and their laws is algebraic
thinking
• Thinking in terms of concrete implementations
(pattern match with Some/None) is
operational thinking
Module with an algebra
trait Monoid[A] {
def zero: A
def combine(l: A, r: !=> A): A
}
!//identity
combine(x, zero) =
combine(zero, x) = x
!// associativity
combine(x, combine(y, z)) =
combine(combine(x, y), z)
Module with an Algebra
trait Foldable[F[_]] {
def foldl[A, B](as: F[A], z: B, f: (B, A) !=> B): B
def foldMap[A, B](as: F[A], f: A !=> B)
(implicit m: Monoid[B]): B =
foldl(as,
m.zero,
(b: B, a: A) !=> m.combine(b, f(a))
)
}
def mapReduce[F[_], A, B](as: F[A], f: A !=> B)
(implicit ff: Foldable[F], m: Monoid[B]) =
ff.foldMap(as, f)
https://stackoverflow.com/a/4765918
def mapReduce[F[_], A, B](as: F[A], f: A !=> B)
(implicit ff: Foldable[F], m: Monoid[B]) =
ff.foldMap(as, f)
https://stackoverflow.com/a/4765918
a complete map/reduce program abstracted as a
functional form
def mapReduce[F[_], A, B](as: F[A], f: A !=> B)
(implicit ff: Foldable[F], m: Monoid[B]) =
ff.foldMap(as, f)
https://stackoverflow.com/a/4765918
a complete map/reduce program abstracted as a
functional form
derived intuitively from the algebras of a fold and a
monoid
Building and understanding higher order abstractions is
much more intuitive using algebraic than operational
thinking
Building and understanding higher order abstractions is
much more intuitive using algebraic than operational
thinking
algebraic thinking scales
Healthy recipes for an
algebra
(in a statically typed functional programming language)
Polymorphic
trait Monoid[A] {
def zero: A
def combine(l: A, r: !=> A): A
}
Lawful
!//identity
combine(x, zero) =
combine(zero, x) = x
!// associativity
combine(x, combine(y, z)) =
combine(combine(x, y), z)
Compositional
trait Foldable[F[_]] {
def foldl[A, B](as: F[A], z: B,
f: (B, A) !=> B): B
def foldMap[A, B](as: F[A], f: A !=> B)
(implicit m: Monoid[B]): B =
foldl(as, m.zero,
(b: B, a: A) !=> m.combine(b, f(a)))
}
Restricted
def mapReduce[F[_], A, B](as: F[A],
f: A !=> B)
(implicit ff: Foldable[F],
m: Monoid[B]) =
ff.foldMap(as, f)
f:A!=>B and g:B!=>C, we should
be able to reason that we can
compose f and g algebraically
to build a larger function h:A!=>C
Implementation
Independent
Open
trait Repository[M[_]] {
def query[A](key: String): M[Option[A]]
!//!..
}
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)
https://msdn.microsoft.com/en-us/library/jj591560.aspx
A Bounded Context
• has a consistent vocabulary
• a set of domain behaviors modeled as
functions on domain objects
implemented as types
• each of the behaviors honor a set of
business rules
• related behaviors grouped as modules
Domain Model = ∪(i) Bounded Context(i)
Bounded Context = { m[T1,T2,..] | T(i) ∈ Types }
Module = { f(x,y,..) | p(x,y) ∈ Domain Rules }
• domain function
• on an object of types x, y, ..
• composes with other functions
• closed under composition
• business rules
Domain Model = ∪(i) Bounded Context(i)
Bounded Context = { m[T1,T2,..] | T(i) ∈ Types }
Module = { f(x,y,..) | p(x,y) ∈ Domain Rules }
• domain function
• on an object of types x, y, ..
• composes with other functions
• closed under composition
• business rules
Domain Algebra
Domain Algebra
Client places order
- flexible format
1
Client places order
- flexible format
Transform to internal domain
model entity and place for execution
1 2
Client places order
- flexible format
Transform to internal domain
model entity and place for execution
Trade & Allocate to
client accounts
1 2
3
def orders(csvOrder: String): M[List[Order]]
def execute(orders: List[Order],
market: Market,
brokerAccountNo: AccountNo)
: M[List[Execution]]
def allocate(executions: List[Execution],
clientAccounts: List[AccountNo])
: M[List[Trade]]
trait Trading[M[_]] {
}
Effect Type that parameterizes
the Trading algebra
def orders(csvOrder: String): M[NonEmptyList[Order]]
def execute(orders: NonEmptyList[Order],
market: Market,
brokerAccountNo: AccountNo)
: M[NonEmptyList[Execution]]
def allocate(executions: NonEmptyList[Execution],
clientAccounts: NonEmptyList[AccountNo])
: M[NonEmptyList[Trade]]
trait Trading[M[_]] {
}
Effect Type that parameterizes
the Trading algebra
Effects
• an algebraic way of handling computational
effects like non-determinism, probabilistic non-
determinism, exceptions, interactive input-
output, side-effects, continuations etc.
• first formalized by Plotkin and Power [2003]
Option[A]
Either[A,B]
(partiality)
(disjunction)
List[A]
(non-determinism)
Reader[E,A]
(read from environment aka dependency Injection)
Writer[W,A]
(logging)
State[S,A]
(state management)
IO[A]
(external side-effects)
.. and there are many many more ..
F[A]
The answer that the
effect computes
The additional stuff
modeling the computation
• Error handling ?
• throw / catch exceptions is not RT
• Partiality ?
• partial functions can report runtime exceptions if
invoked with unhandled arguments (violates RT)
• Reading configuration information from environment ?
• may result in code repetition if not properly handled
• Logging ?
Side-effects
Side-effects
• Database writes
• Writing to a message queue
• Reading from stdin / files
• Interacting with any external resource
• Changing state in place
modularity
side-effects don’t compose
def orders(csvOrder: String): M[NonEmptyList[Order]]
def execute(orders: NonEmptyList[Order],
market: Market,
brokerAccountNo: AccountNo)
: M[NonEmptyList[Execution]]
def allocate(executions: NonEmptyList[Execution],
clientAccounts: NonEmptyList[AccountNo])
: M[NonEmptyList[Trade]]
trait Trading[M[_]] {
}
Effect Types offer compositionality even in the
presence of side-effects
All M[_]’s indicate that some
computation is going on here
• The M[_] that we saw is an opaque type - it
has no denotation till we give it one
• The denotation that we give to M[_] depends
on the semantics of compositionality that we
would like to have for our domain model
behaviors
The Program
def generateTrade[M[_]: Monad](T: Trading[M]) = for {
orders !<- T.orders(csvOrders)
executions !<- T.execute(orders, Market.NewYork, brokerAccountNo)
trades !<- T.allocate(executions, clientAccountNos)
} yield trades
The Program
def generateTrade[M[_]: Monad](T: Trading[M]) = for {
orders !<- T.orders(csvOrders)
executions !<- T.execute(orders, Market.NewYork, brokerAccountNo)
trades !<- T.allocate(executions, clientAccountNos)
} yield trades
Composition of the algebra of a Monad with our domain algebra of trading
Parametricity
• Trading module is polymorphic on M[_].We could
have committed to Trading[IO] upfront - but then
we are making decisions on behalf of the call site.This is
premature evaluation
• In implementation we can say M[_]: Monad and
suddenly the only operations available to us are pure
and !flatMap.This reduces the surface area of
implementation.With IO we could have done anything
in the implementation.
def postBalance(trades: NonEmptyList[Trade]
: F[NonEmptyList[Balance]]
trait Accounting[M[_]] {
}
Effect Type that parameterizes
the Accounting algebra
The Program
def generateTradeAndPostBalance[M[_]:Monad]
(T:Trading[M], A:Accounting[M]) = for {
orders !<- T.orders(csvOrders)
executions !<- T.execute(orders, Market.NewYork, brokerAccountNo)
trades !<- T.allocate(executions, clientAccountNos)
balances !<- A.postBalance(trades)
} yield (trades, balances)
The Program
def generateTradeAndPostBalance[M[_]:Monad]
(T:Trading[M], A:Accounting[M]) = for {
orders !<- T.orders(csvOrders)
executions !<- T.execute(orders, Market.NewYork, brokerAccountNo)
trades !<- T.allocate(executions, clientAccountNos)
balances !<- A.postBalance(trades)
} yield (trades, balances)
Composition of multiple domain algebras
• .. we have intentionally kept the algebra open
for interpretation ..
• .. there are use cases where you would like to
have multiple interpreters for the same
algebra ..
Interpreters
class TradingInterpreter[M[+_]]
(implicit E: MonadError[M, Throwable],
R: ApplicativeAsk[M, Repository[M]])
extends Trading[M] {
!//!..
}
monad with error handling
asks for a repository
from the environment
Interpreters
class TradingInterpreter[M[+_]]
(implicit E: MonadError[M, Throwable],
R: ApplicativeAsk[M, Repository[M]])
extends Trading[M] {
!//!..
}
monad with error handling
asks for a repository
from the environment
InMemoryRepository[M]
DoobieRepository[M]
Finally ..
implicit val !.. = !//!..
generateTradeAndPostBalances(
new TradingInterpreter[IO],
new AccountingInterpreter[IO]
)
Effects
Side-effects
- Rob Norris at scale.bythebay.io talk - 2017 (https://www.youtube.com/
watch?v=po3wmq4S15A)
“Effects and side-effects are not the same thing. Effects are
good, side-effects are bugs.Their lexical similarity is really
unfortunate because people often conflate the two ideas”
Takeaways
• Algebra scales from that of one single data type to
an entire bounded context
• Algebras compose enabling composition of
domain behaviors
• Algebras let you focus on the compositionality
without any context of implementation
• Statically typed functional programming is
programming with algebras
Takeaways
• Abstract early, interpret as late as possible
• Abstractions / functions compose only when they are
abstract and parametric
• Modularity in the presence of side-effects is a challenge
• Effects as algebras are pure values that can compose based
on laws
• Honor the law of using the least powerful abstraction
that works
Questions?
References
• Scott, D., and Strachey, C. [1971 ]. Towards a mathematical semantics for computer languages.
Proc. Symp. on Computers and Automata, Polytechnic Institute of Brooklyn; also Tech. Mon. PRG-6, Oxford U. Computing Lab., pp.
19-46.
• Gordon Plotkin, and John Power. “Algebraic Operations and Generic Effects.” Applied Categorical Structures 11 (1): 69–94. 2003.
doi:10.1023/A:1023064908962.

Mais conteúdo relacionado

Mais procurados

Mais procurados (20)

Power of functions in a typed world
Power of functions in a typed worldPower of functions in a typed world
Power of functions in a typed world
 
Preparing for Scala 3
Preparing for Scala 3Preparing for Scala 3
Preparing for Scala 3
 
Fusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with ViewsFusing Transformations of Strict Scala Collections with Views
Fusing Transformations of Strict Scala Collections with Views
 
Scala Intro
Scala IntroScala Intro
Scala Intro
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain Modeling
 
Quick introduction to scala
Quick introduction to scalaQuick introduction to scala
Quick introduction to scala
 
Monoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and CatsMonoids - Part 1 - with examples using Scalaz and Cats
Monoids - Part 1 - with examples using Scalaz and Cats
 
ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022ZIO-Direct - Functional Scala 2022
ZIO-Direct - Functional Scala 2022
 
Sequence and Traverse - Part 2
Sequence and Traverse - Part 2Sequence and Traverse - Part 2
Sequence and Traverse - Part 2
 
Quill vs Slick Smackdown
Quill vs Slick SmackdownQuill vs Slick Smackdown
Quill vs Slick Smackdown
 
The Expression Problem - Part 1
The Expression Problem - Part 1The Expression Problem - Part 1
The Expression Problem - Part 1
 
The Evolution of Scala
The Evolution of ScalaThe Evolution of Scala
The Evolution of Scala
 
Introduction à scala
Introduction à scalaIntroduction à scala
Introduction à scala
 
A Brief Intro to Scala
A Brief Intro to ScalaA Brief Intro to Scala
A Brief Intro to Scala
 
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...Algebraic Data Types forData Oriented Programming - From Haskell and Scala t...
Algebraic Data Types for Data Oriented Programming - From Haskell and Scala t...
 
Boost your productivity with Scala tooling!
Boost your productivity  with Scala tooling!Boost your productivity  with Scala tooling!
Boost your productivity with Scala tooling!
 
Functional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modelingFunctional and Event Driven - another approach to domain modeling
Functional and Event Driven - another approach to domain modeling
 
Apache Spark Architecture
Apache Spark ArchitectureApache Spark Architecture
Apache Spark Architecture
 
Cost-based Query Optimization in Apache Phoenix using Apache Calcite
Cost-based Query Optimization in Apache Phoenix using Apache CalciteCost-based Query Optimization in Apache Phoenix using Apache Calcite
Cost-based Query Optimization in Apache Phoenix using Apache Calcite
 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 1
 

Semelhante a Algebraic Thinking for Evolution of Pure Functional Domain Models

Programming in python
Programming in pythonProgramming in python
Programming in python
Ivan Rojas
 

Semelhante a Algebraic Thinking for Evolution of Pure Functional Domain Models (20)

Architectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain ModelsArchitectural Patterns in Building Modular Domain Models
Architectural Patterns in Building Modular Domain Models
 
An Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain ModelingAn Algebraic Approach to Functional Domain Modeling
An Algebraic Approach to Functional Domain Modeling
 
Mining Functional Patterns
Mining Functional PatternsMining Functional Patterns
Mining Functional Patterns
 
Functional Scala
Functional ScalaFunctional Scala
Functional Scala
 
Functional IO and Effects
Functional IO and EffectsFunctional IO and Effects
Functional IO and Effects
 
Why functional programming and category theory strongly matters - Piotr Parad...
Why functional programming and category theory strongly matters - Piotr Parad...Why functional programming and category theory strongly matters - Piotr Parad...
Why functional programming and category theory strongly matters - Piotr Parad...
 
Why functional programming and category theory strongly matters
Why functional programming and category theory strongly mattersWhy functional programming and category theory strongly matters
Why functional programming and category theory strongly matters
 
Functional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic ProgrammerFunctional Programming Patterns for the Pragmatic Programmer
Functional Programming Patterns for the Pragmatic Programmer
 
Programming in python
Programming in pythonProgramming in python
Programming in python
 
Oh, All the things you'll traverse
Oh, All the things you'll traverseOh, All the things you'll traverse
Oh, All the things you'll traverse
 
Mcs 011 solved assignment 2015-16
Mcs 011 solved assignment 2015-16Mcs 011 solved assignment 2015-16
Mcs 011 solved assignment 2015-16
 
Functions, Types, Programs and Effects
Functions, Types, Programs and EffectsFunctions, Types, Programs and Effects
Functions, Types, Programs and Effects
 
Functors
FunctorsFunctors
Functors
 
Actors and functional_reactive_programming
Actors and functional_reactive_programmingActors and functional_reactive_programming
Actors and functional_reactive_programming
 
Monads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy DyagilevMonads and Monoids by Oleksiy Dyagilev
Monads and Monoids by Oleksiy Dyagilev
 
Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...
 
Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...Big picture of category theory in scala with deep dive into contravariant and...
Big picture of category theory in scala with deep dive into contravariant and...
 
C-PPT.pdf
C-PPT.pdfC-PPT.pdf
C-PPT.pdf
 
Functional Programming in the Wild
Functional Programming in the WildFunctional Programming in the Wild
Functional Programming in the Wild
 
Functional programming with FSharp
Functional programming with FSharpFunctional programming with FSharp
Functional programming with FSharp
 

Mais de Debasish Ghosh

Mais de Debasish Ghosh (8)

Approximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming ApplicationsApproximation Data Structures for Streaming Applications
Approximation Data Structures for Streaming Applications
 
Functional and Algebraic Domain Modeling
Functional and Algebraic Domain ModelingFunctional and Algebraic Domain Modeling
Functional and Algebraic Domain Modeling
 
From functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modelingFrom functional to Reactive - patterns in domain modeling
From functional to Reactive - patterns in domain modeling
 
Domain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approachDomain Modeling with Functions - an algebraic approach
Domain Modeling with Functions - an algebraic approach
 
Property based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rulesProperty based Testing - generative data & executable domain rules
Property based Testing - generative data & executable domain rules
 
Big Data - architectural concerns for the new age
Big Data - architectural concerns for the new ageBig Data - architectural concerns for the new age
Big Data - architectural concerns for the new age
 
DSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic modelDSL - expressive syntax on top of a clean semantic model
DSL - expressive syntax on top of a clean semantic model
 
Dependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake PatternDependency Injection in Scala - Beyond the Cake Pattern
Dependency Injection in Scala - Beyond the Cake Pattern
 

Último

Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
panagenda
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
WSO2
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Último (20)

WSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering DevelopersWSO2's API Vision: Unifying Control, Empowering Developers
WSO2's API Vision: Unifying Control, Empowering Developers
 
Why Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire businessWhy Teams call analytics are critical to your entire business
Why Teams call analytics are critical to your entire business
 
Architecting Cloud Native Applications
Architecting Cloud Native ApplicationsArchitecting Cloud Native Applications
Architecting Cloud Native Applications
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Platformless Horizons for Digital Adaptability
Platformless Horizons for Digital AdaptabilityPlatformless Horizons for Digital Adaptability
Platformless Horizons for Digital Adaptability
 
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot TakeoffStrategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
Strategize a Smooth Tenant-to-tenant Migration and Copilot Takeoff
 
Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)Introduction to Multilingual Retrieval Augmented Generation (RAG)
Introduction to Multilingual Retrieval Augmented Generation (RAG)
 
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024Finding Java's Hidden Performance Traps @ DevoxxUK 2024
Finding Java's Hidden Performance Traps @ DevoxxUK 2024
 
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin WoodPolkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
 
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
Navigating the Deluge_ Dubai Floods and the Resilience of Dubai International...
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
CNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In PakistanCNIC Information System with Pakdata Cf In Pakistan
CNIC Information System with Pakdata Cf In Pakistan
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..Understanding the FAA Part 107 License ..
Understanding the FAA Part 107 License ..
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf[BuildWithAI] Introduction to Gemini.pdf
[BuildWithAI] Introduction to Gemini.pdf
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot ModelMcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
Mcleodganj Call Girls 🥰 8617370543 Service Offer VIP Hot Model
 

Algebraic Thinking for Evolution of Pure Functional Domain Models

  • 1. Algebraic Thinking for Evolution of Pure Functional Domain Models Debasish Ghosh @debasishg
  • 2. Traveling back in time .. more than 40 years .. Copyright: https://www.npr.org/2014/06/17/318605598/turn-the-clock-back-or-forward-with-time-traveling-tales
  • 3.
  • 4.
  • 5. c !:= 0 for i !:= 1 step 1 until n do c !:= c + a[i] x b[i] Von Neumann program for Inner Product “It is dynamic and repetitive. One must mentally execute it to understand it” - John Backus
  • 6. Def Innerproduct = (Insert +) o (ApplyToAll X) o Transpose Composition (o), Insert, ApplyToAll etc. are functional forms that combine existing functions to form new ones Functional program for Inner Product “It’s structure is helpful in understanding it without mentally executing it” - John Backus
  • 7. - John Backus “.. programs can be expressed in a language that has an associated algebra. This algebra can be used to transform programs and to solve some equations whose " unknowns" are programs, in much the same way one solves equations in high school algebra. Algebraic transformations and proofs use the language of the programs themselves, rather than the language of logic, which talks about programs.”
  • 8. What is an Algebra ? Algebra is the study of algebraic structures In mathematics, and more specifically in abstract algebra, an algebraic structure is a set (called carrier set or underlying set) with one or more finitary operations defined on it that satisfies a list of axioms -Wikipedia (https://en.wikipedia.org/wiki/Algebraic_structure)
  • 9. SetA ϕ : A × A → A for (a, b) ∈ A ϕ(a, b) a ϕ b given a binary operation for specific a, b or The Algebra of Sets
  • 10. Algebraic Thinking • Denotational Semantics ✦ programs and the objects they manipulate are symbolic realizations of abstract mathematical objects ✦ the purpose of a mathematical semantics is to give a correct and meaningful correspondence between programs and mathematical entities in a way that is entirely independent of an implementation [Scott & Strachey, 1971]
  • 12. Operational Thinking • Operational Semantics ✦ formalize program implementation and how the various functions must be computed or represented ✦ not much of a relevance towards algebraic reasoning
  • 13. Option.apply[A](a: A): Option[A] Option.empty[A]: Option[A] Option[A] A: Carrier Type of the algebra Introduction Forms
  • 14. Option.apply[A](a: A): Option[A] Option.empty[A]: Option[A] def f[A, B](func: A !=> B) = ??? optionA.map(f) !// Option[B] def f[A, B](func: A !=> Option[B]) = ??? optionA.!flatMap(f) !// Option[B] Option[A] A: Carrier Type of the algebra Introduction Forms Combinators
  • 15. Option.apply[A](a: A): Option[A] Option.empty[A]: Option[A] def f[A, B](func: A !=> B) = ??? optionA.map(f) !// Option[B] def f[A, B](func: A !=> Option[B]) = ??? optionA.!flatMap(f) !// Option[B] optionA.getOrElse(default) !// A or B !>: A Option[A] A: Carrier Type of the algebra Introduction Forms Combinators Eliminator Forms
  • 16. Option.apply[A](a: A): Option[A] Option.empty[A]: Option[A] def f[A, B](func: A !=> B) = ??? optionA.map(f) !// Option[B] def f[A, B](func: A !=> Option[B]) = ??? optionA.!flatMap(f) !// Option[B] optionA.getOrElse(default) !// A or B !>: A Option.empty[Int].!flatMap(!!...) !== Option.empty[Int] !// res1: Boolean = true Option.empty[Int].map(!!...) !== Option.empty[Int] !// res2: Boolean = true Option[A] A: Carrier Type of the algebra Introduction Forms Combinators Eliminator Forms Laws
  • 17. A: Carrier Type of the algebra Introduction Forms Combinators Eliminator Forms Laws algebra
  • 18. • Thinking in terms of combinators (map/ !flatMap/fold) and their laws is algebraic thinking • Thinking in terms of concrete implementations (pattern match with Some/None) is operational thinking
  • 19. Module with an algebra trait Monoid[A] { def zero: A def combine(l: A, r: !=> A): A } !//identity combine(x, zero) = combine(zero, x) = x !// associativity combine(x, combine(y, z)) = combine(combine(x, y), z)
  • 20. Module with an Algebra trait Foldable[F[_]] { def foldl[A, B](as: F[A], z: B, f: (B, A) !=> B): B def foldMap[A, B](as: F[A], f: A !=> B) (implicit m: Monoid[B]): B = foldl(as, m.zero, (b: B, a: A) !=> m.combine(b, f(a)) ) }
  • 21. def mapReduce[F[_], A, B](as: F[A], f: A !=> B) (implicit ff: Foldable[F], m: Monoid[B]) = ff.foldMap(as, f) https://stackoverflow.com/a/4765918
  • 22. def mapReduce[F[_], A, B](as: F[A], f: A !=> B) (implicit ff: Foldable[F], m: Monoid[B]) = ff.foldMap(as, f) https://stackoverflow.com/a/4765918 a complete map/reduce program abstracted as a functional form
  • 23. def mapReduce[F[_], A, B](as: F[A], f: A !=> B) (implicit ff: Foldable[F], m: Monoid[B]) = ff.foldMap(as, f) https://stackoverflow.com/a/4765918 a complete map/reduce program abstracted as a functional form derived intuitively from the algebras of a fold and a monoid
  • 24. Building and understanding higher order abstractions is much more intuitive using algebraic than operational thinking
  • 25. Building and understanding higher order abstractions is much more intuitive using algebraic than operational thinking algebraic thinking scales
  • 26. Healthy recipes for an algebra (in a statically typed functional programming language)
  • 27. Polymorphic trait Monoid[A] { def zero: A def combine(l: A, r: !=> A): A }
  • 28. Lawful !//identity combine(x, zero) = combine(zero, x) = x !// associativity combine(x, combine(y, z)) = combine(combine(x, y), z)
  • 29. Compositional trait Foldable[F[_]] { def foldl[A, B](as: F[A], z: B, f: (B, A) !=> B): B def foldMap[A, B](as: F[A], f: A !=> B) (implicit m: Monoid[B]): B = foldl(as, m.zero, (b: B, a: A) !=> m.combine(b, f(a))) }
  • 30. Restricted def mapReduce[F[_], A, B](as: F[A], f: A !=> B) (implicit ff: Foldable[F], m: Monoid[B]) = ff.foldMap(as, f)
  • 31. f:A!=>B and g:B!=>C, we should be able to reason that we can compose f and g algebraically to build a larger function h:A!=>C Implementation Independent
  • 32. Open trait Repository[M[_]] { def query[A](key: String): M[Option[A]] !//!.. }
  • 33. 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)
  • 35. A Bounded Context • has a consistent vocabulary • a set of domain behaviors modeled as functions on domain objects implemented as types • each of the behaviors honor a set of business rules • related behaviors grouped as modules
  • 36. Domain Model = ∪(i) Bounded Context(i) Bounded Context = { m[T1,T2,..] | T(i) ∈ Types } Module = { f(x,y,..) | p(x,y) ∈ Domain Rules } • domain function • on an object of types x, y, .. • composes with other functions • closed under composition • business rules
  • 37. Domain Model = ∪(i) Bounded Context(i) Bounded Context = { m[T1,T2,..] | T(i) ∈ Types } Module = { f(x,y,..) | p(x,y) ∈ Domain Rules } • domain function • on an object of types x, y, .. • composes with other functions • closed under composition • business rules Domain Algebra Domain Algebra
  • 38. Client places order - flexible format 1
  • 39. Client places order - flexible format Transform to internal domain model entity and place for execution 1 2
  • 40. Client places order - flexible format Transform to internal domain model entity and place for execution Trade & Allocate to client accounts 1 2 3
  • 41. def orders(csvOrder: String): M[List[Order]] def execute(orders: List[Order], market: Market, brokerAccountNo: AccountNo) : M[List[Execution]] def allocate(executions: List[Execution], clientAccounts: List[AccountNo]) : M[List[Trade]] trait Trading[M[_]] { } Effect Type that parameterizes the Trading algebra
  • 42. def orders(csvOrder: String): M[NonEmptyList[Order]] def execute(orders: NonEmptyList[Order], market: Market, brokerAccountNo: AccountNo) : M[NonEmptyList[Execution]] def allocate(executions: NonEmptyList[Execution], clientAccounts: NonEmptyList[AccountNo]) : M[NonEmptyList[Trade]] trait Trading[M[_]] { } Effect Type that parameterizes the Trading algebra
  • 43. Effects • an algebraic way of handling computational effects like non-determinism, probabilistic non- determinism, exceptions, interactive input- output, side-effects, continuations etc. • first formalized by Plotkin and Power [2003]
  • 44. Option[A] Either[A,B] (partiality) (disjunction) List[A] (non-determinism) Reader[E,A] (read from environment aka dependency Injection) Writer[W,A] (logging) State[S,A] (state management) IO[A] (external side-effects) .. and there are many many more ..
  • 45. F[A] The answer that the effect computes The additional stuff modeling the computation
  • 46. • Error handling ? • throw / catch exceptions is not RT • Partiality ? • partial functions can report runtime exceptions if invoked with unhandled arguments (violates RT) • Reading configuration information from environment ? • may result in code repetition if not properly handled • Logging ? Side-effects
  • 47. Side-effects • Database writes • Writing to a message queue • Reading from stdin / files • Interacting with any external resource • Changing state in place
  • 49. def orders(csvOrder: String): M[NonEmptyList[Order]] def execute(orders: NonEmptyList[Order], market: Market, brokerAccountNo: AccountNo) : M[NonEmptyList[Execution]] def allocate(executions: NonEmptyList[Execution], clientAccounts: NonEmptyList[AccountNo]) : M[NonEmptyList[Trade]] trait Trading[M[_]] { } Effect Types offer compositionality even in the presence of side-effects All M[_]’s indicate that some computation is going on here
  • 50. • The M[_] that we saw is an opaque type - it has no denotation till we give it one • The denotation that we give to M[_] depends on the semantics of compositionality that we would like to have for our domain model behaviors
  • 51. The Program def generateTrade[M[_]: Monad](T: Trading[M]) = for { orders !<- T.orders(csvOrders) executions !<- T.execute(orders, Market.NewYork, brokerAccountNo) trades !<- T.allocate(executions, clientAccountNos) } yield trades
  • 52. The Program def generateTrade[M[_]: Monad](T: Trading[M]) = for { orders !<- T.orders(csvOrders) executions !<- T.execute(orders, Market.NewYork, brokerAccountNo) trades !<- T.allocate(executions, clientAccountNos) } yield trades Composition of the algebra of a Monad with our domain algebra of trading
  • 53. Parametricity • Trading module is polymorphic on M[_].We could have committed to Trading[IO] upfront - but then we are making decisions on behalf of the call site.This is premature evaluation • In implementation we can say M[_]: Monad and suddenly the only operations available to us are pure and !flatMap.This reduces the surface area of implementation.With IO we could have done anything in the implementation.
  • 54. def postBalance(trades: NonEmptyList[Trade] : F[NonEmptyList[Balance]] trait Accounting[M[_]] { } Effect Type that parameterizes the Accounting algebra
  • 55. The Program def generateTradeAndPostBalance[M[_]:Monad] (T:Trading[M], A:Accounting[M]) = for { orders !<- T.orders(csvOrders) executions !<- T.execute(orders, Market.NewYork, brokerAccountNo) trades !<- T.allocate(executions, clientAccountNos) balances !<- A.postBalance(trades) } yield (trades, balances)
  • 56. The Program def generateTradeAndPostBalance[M[_]:Monad] (T:Trading[M], A:Accounting[M]) = for { orders !<- T.orders(csvOrders) executions !<- T.execute(orders, Market.NewYork, brokerAccountNo) trades !<- T.allocate(executions, clientAccountNos) balances !<- A.postBalance(trades) } yield (trades, balances) Composition of multiple domain algebras
  • 57. • .. we have intentionally kept the algebra open for interpretation .. • .. there are use cases where you would like to have multiple interpreters for the same algebra ..
  • 58. Interpreters class TradingInterpreter[M[+_]] (implicit E: MonadError[M, Throwable], R: ApplicativeAsk[M, Repository[M]]) extends Trading[M] { !//!.. } monad with error handling asks for a repository from the environment
  • 59. Interpreters class TradingInterpreter[M[+_]] (implicit E: MonadError[M, Throwable], R: ApplicativeAsk[M, Repository[M]]) extends Trading[M] { !//!.. } monad with error handling asks for a repository from the environment InMemoryRepository[M] DoobieRepository[M]
  • 60. Finally .. implicit val !.. = !//!.. generateTradeAndPostBalances( new TradingInterpreter[IO], new AccountingInterpreter[IO] )
  • 62. - Rob Norris at scale.bythebay.io talk - 2017 (https://www.youtube.com/ watch?v=po3wmq4S15A) “Effects and side-effects are not the same thing. Effects are good, side-effects are bugs.Their lexical similarity is really unfortunate because people often conflate the two ideas”
  • 63. Takeaways • Algebra scales from that of one single data type to an entire bounded context • Algebras compose enabling composition of domain behaviors • Algebras let you focus on the compositionality without any context of implementation • Statically typed functional programming is programming with algebras
  • 64. Takeaways • Abstract early, interpret as late as possible • Abstractions / functions compose only when they are abstract and parametric • Modularity in the presence of side-effects is a challenge • Effects as algebras are pure values that can compose based on laws • Honor the law of using the least powerful abstraction that works
  • 65.
  • 67. References • Scott, D., and Strachey, C. [1971 ]. Towards a mathematical semantics for computer languages. Proc. Symp. on Computers and Automata, Polytechnic Institute of Brooklyn; also Tech. Mon. PRG-6, Oxford U. Computing Lab., pp. 19-46. • Gordon Plotkin, and John Power. “Algebraic Operations and Generic Effects.” Applied Categorical Structures 11 (1): 69–94. 2003. doi:10.1023/A:1023064908962.