Beyond PITS,
Functional Principles for
SoftwareArchitecture
jayas@cisco.com
07 June 2018
Foreword This talk includes:
Sample code / code walk thru in Haskell / Elm / Scala /
Java , but you need not know the language.
Focus is mostly on principles and concepts.
This talk assumes :
You are an user of Java / C++ and are familiar with
object oriented programming concepts.
Software
Architecture
PITS
Programming in the small
Functional
Principles
What is a
Function?
y = f(x)
x yf
input outputfunction
What are these?Types
f ::
Functions
Everywhere
f
f :: Int -> Int -> String -> Int -> String( ) ( )
Int
Int -> String
Function as
Parameter
Function as return
type
Int -> String
Functions
Everywhere
2’s table
What will ‘twoTimes 3’ return?Q
2 X 3 = 6
What are
Types?
Integer
Char
String
String -> String
Set of valid Input /
Output
Describes Data
(No Behavior)
What are
Types?
f
f :: Person -> String
Person String
f p = “Name : “ ++ (name p)
Person is aType String is aType
Types can have parameters , typically the data it
holds.
data Person = Person {name :: String}
User Defined
Types
data Role = Staff | Supervisor | Admin
UnionTypes
The valid types are additive.
Role is Staff or Supervisor or
Admin
type Name = String
data Employee = Employee Name Role
Product Types
Combination of types.
Employee has Name and Role
Pure Functions
No side effects , including IO
• For a set of inputs, always produce the same output
formatName( ) {
name = name.toLower();
}
String formatName(name x ) {
return x.toLower()
}
Pure Functions
String formatName(String name ) {
if(name.length > maxLen) {
return name.substring(maxLen).toLower()
} else {
return name.toLower()
}
}
String formatName(String name , int maxLen) {
if(name.length > maxLen) {
return name.substring(maxLen).toLower()
} else {
return name.toLower()
}
}
Q
Pure Functions
String formatName(String name , int maxLen) {
if(name.length > maxLen) {
log(“truncating name”)
return name.substring(maxLen).toLower()
} else {
return name.toLower()
}
}
Why Pure
Functions?
Testability
• No additional/hidden dependencies
• Easy to reason about and understand
• Does only what its supposed to do every
time, every where
• Utilize property based testing
• quickcheck – for Haskell
• scalacheck
• fast-check – for JS
Enables all the Functional Programming capabilities
High quality, predictable and reliable code
Function
Composition
grep “HardToFindError” mylog.log | wc –l
tail –f mylog.log | grep “HardToFindError”
Create new useful stuff by using the building
blocks that are already present
Function
Composition
nameOf
User
String String
format
String
String
formatedNameOf
User
formatedNameOf :: User -> String
fromatedNameOf = format . nameOf
User
String
nameOf format
formatedNameOf
f .g = f(g(x))
Function
Composition
nameOf
User
String Int String
format
String
All functions are single parameter functions
format :: Int -> String -> String
myformat :: String -> String
myformat = format 15
User
String
nameOf myformat
formatedNameOfUser
String
nameOf format
formatedNameOf
Programming
byWhole
Values
• Programming ByWordVsWhole
Values
• No word at a time processing
• Work with entire value (data
structures)
• How to DoVsWhatTo Do
• Focus on business logic
implementation
• e.g: No instructions on how to
traverse a list
for(int i=0; i< j; i++)
{
....
}
Programming
byWhole
Values
Do a map, filter and fold it!
map (User ->String) -> List User -> List String
● List of Users -> List of User Names
flatMap (User -> List String ) -> List User -> List String
● List of Users -> List of Phone Numbers
filter (User->Boolean) -> List<User> -> List<User>
● Get all Admins from user list
foldl ( Int -> User -> Int) -> Int -> List User -> Int
● Accumulate a value , say bill amount, from a list of all Users
String Int User
Normal World
The Higher
World
List String
Higher World
Maybe Int List User
User -> String
List User
Lift to the HigherWorld
List.map (User -> String)
List User -> List String
Functors, Applicatives,
Monads
Functor
Q
String Int User
Normal World
The Higher
World
List String
Higher World
Maybe Int List User
Transformation 1 Transformation N
find :: String -> Maybe User
How to get
#phone
numbers
configured for
the user?
Option 1
Maybe
User
User
Maybe
Phones
Phones
Maybe
Count
Option 2
Maybe
User
Maybe
Phones
Maybe
Count
flatMap map
Immutability
Reference to a data structure is *only* for reading
from the data structure
Once you have a reference:
• Guarantees that no one will modify its contents
• Pass along to any number of threads without fear of ‘stepping on each
other’
• Enables safe concurrency and parallelism
• Any updates to the data structure returns a new reference
Immutability
Defects (holds list of open defects)
val defectList =
List(Defect(id="CSSid1234",age=48,engineer="jayas"),Defect(id="CSCid5678",age=12,engineer="otherone"))
defectList.foreach { defect =>
if (defect.age > 28) {
sendMsg(defect.engineer)
}
}
defectList.dropWhile {
defect =>
defect.engineer == "jayas"
}
Thread 1 Thread 2
val myDefectList =
Functional
Design
Patterns
Dependency Injection
Strategy Pattern
Decorator Pattern
...
Function
More on this is a
separate talk!
Q
PureSystems
AreUseless
f
f
f
f
f
f
If no IO can happen, no data can be modified , these
collection of pure functions are useless!
What is the way forward?
List String
Higher World
Maybe Int List User
Functional IO
State Management IO (Network / Disk)
All Impure operations are bounded in IO context , with the
context composing with other contexts there by chaining
operations together.
IO / State
Action
IO/State
Effect
IO / State
Value
Architecture :
Functional
Influences
Impure
Boundaries,
PureCore
Pure Functions
80%
20%
IOIO
IOIO
Design
Principles in
theCore
1 Parameterize Inputs
String formatName(String) {
if(name.length > maxLen) {
return
name.substring(maxLen).toLower()
} else {
return name.toLower()
}
}
String formatName(String name , int
maxLen) {
if(name.length > maxLen) {
return
name.substring(maxLen).toLower()
} else {
return name.toLower()
}
}
Do not keep global mutable state / vars
Design
Principles in
theCore
2 Use Composition
nameOf
User
String String
format
String
User
String
nameOf format
formatedNameOf
Design
Principles in
theCore
3 Functions Everywhere
Function as
Parameter
Function as return
type
f
f :: Int -> Int -> String -> Int -> String( ) ( )
Int
Int -> String
Int -> String
Design
Principles in
theCore
4 Ensure Totality (Use Types)
String getAlias(User u ) {
if(u….) {
return u.XYZ
} else {
return null
}
}
Optional<String > getAlias(User u) {
return Optional.of(u.XYZ)
}
Q
Design
Principles in
theCore
5 Compositions in the Higher World
IO String
Higher World
Maybe Int List User
Context
Effect /
Value
Context
Effect /
Value
Context
Effect /
Value
Use transformation functions for chaining operations
together.
EvolutionUse
Case
config
Chat Router
webhook
API
API
fbm.js
Fbm gateway
Web Chat
Avoiding
Global State
Make 80% of
Functions Pure
Code WalkThru
Stateless
to
Serverless
Stateless
processStateless
process
Stateless
process
Storage
Functions!
Notifications
Q
Functional
Reactive
Architecture
elm
ExternalWorld
• User Actions
• Timed Events
• Browser Events
Elm Runtime
• Task Execution
• Virtual DOM
Model
View
Update Model
Msg
Cmd Msg
Msg
ApplicationElm Runtime
Html Msg
Model : Record Msg :Type
Update : Msg -> Model -
> (Model, Cmd Msg)
View : Model -> Html
Msg
Elm
Architecture
DemoUseCase
Notification Service
Build an UI to send
notifications
Send Message Receive Message
CodeWalkThru
Micro Services
Composition
f1
f2 f3x y
Use Case
u1 u2 U3
Service
req res
System
S1
S2
S4
S3
src sinkQ
Events
Application
Event Sourcing
Immutability Thou shall not change.
Append Only Log
Apache Kafka
Writes
Log Events stateReplay Events
I am not sure..
WhatCan I do?
Adopt these guidelines, if you are Java ( / C++ )
programmer
• Isolate State management and IO to one part of the system
• Let 80 % of the implementation be pure and stateless
• Question and Revisit Impure functions / methods
• Strictly adhere to Single Responsibility Principle of OO
• Write pure functions unless it’s unavoidable
• It’s fine to have a class with all static methods, which are pure!
• Ensure there is only one and only one way a computation can
happen
I am not sure..
WhatCan I do
Adopt these rules, if you are Java ( / C++) programmer
• Use built-in functional constructs / third party libraries for the
following
• No nulls to indicate absence. Use Optional
• Use it to indicate to other that data may or may not be present
• No loops .
• Use Functions in Collection libraries for working on collections
• Use Immutable collections where available
• Avoid shared memory / shared data
• Follow message passing instead
• Ensure only one thread acts on protected data, avoid locks
Disclaimers
• As Always, there are exceptions to all the rules ,
imperative may be best for performance critical parts
of your system
• Performance , Maintainability and Readability are of
most importance
• Do not compromise on these, especially when using the
programming language natively does not support pure
functional constructs
• Design and Implementation of Immutable Data Structures
are critical for data intensive operations
Learn
Functional
Programming
Haskell : https://github.com/data61/fp-course
Haskell for Greater Good : http://learnyouahaskell.com/chapters
Elm : http://elm-lang.org/
Scala : http://docs.scala-lang.org/getting-started.html
Learn
Functional
Programming
Thank You!

Beyond PITS, Functional Principles for Software Architecture

  • 1.
    Beyond PITS, Functional Principlesfor SoftwareArchitecture jayas@cisco.com 07 June 2018
  • 2.
    Foreword This talkincludes: Sample code / code walk thru in Haskell / Elm / Scala / Java , but you need not know the language. Focus is mostly on principles and concepts. This talk assumes : You are an user of Java / C++ and are familiar with object oriented programming concepts.
  • 3.
  • 4.
  • 5.
  • 6.
    What is a Function? y= f(x) x yf input outputfunction What are these?Types f ::
  • 7.
    Functions Everywhere f f :: Int-> Int -> String -> Int -> String( ) ( ) Int Int -> String Function as Parameter Function as return type Int -> String
  • 8.
    Functions Everywhere 2’s table What will‘twoTimes 3’ return?Q 2 X 3 = 6
  • 9.
    What are Types? Integer Char String String ->String Set of valid Input / Output Describes Data (No Behavior)
  • 10.
    What are Types? f f ::Person -> String Person String f p = “Name : “ ++ (name p) Person is aType String is aType Types can have parameters , typically the data it holds. data Person = Person {name :: String}
  • 11.
    User Defined Types data Role= Staff | Supervisor | Admin UnionTypes The valid types are additive. Role is Staff or Supervisor or Admin type Name = String data Employee = Employee Name Role Product Types Combination of types. Employee has Name and Role
  • 12.
    Pure Functions No sideeffects , including IO • For a set of inputs, always produce the same output formatName( ) { name = name.toLower(); } String formatName(name x ) { return x.toLower() }
  • 13.
    Pure Functions String formatName(Stringname ) { if(name.length > maxLen) { return name.substring(maxLen).toLower() } else { return name.toLower() } } String formatName(String name , int maxLen) { if(name.length > maxLen) { return name.substring(maxLen).toLower() } else { return name.toLower() } } Q
  • 14.
    Pure Functions String formatName(Stringname , int maxLen) { if(name.length > maxLen) { log(“truncating name”) return name.substring(maxLen).toLower() } else { return name.toLower() } }
  • 15.
    Why Pure Functions? Testability • Noadditional/hidden dependencies • Easy to reason about and understand • Does only what its supposed to do every time, every where • Utilize property based testing • quickcheck – for Haskell • scalacheck • fast-check – for JS Enables all the Functional Programming capabilities High quality, predictable and reliable code
  • 16.
    Function Composition grep “HardToFindError” mylog.log| wc –l tail –f mylog.log | grep “HardToFindError” Create new useful stuff by using the building blocks that are already present
  • 17.
    Function Composition nameOf User String String format String String formatedNameOf User formatedNameOf ::User -> String fromatedNameOf = format . nameOf User String nameOf format formatedNameOf f .g = f(g(x))
  • 18.
    Function Composition nameOf User String Int String format String Allfunctions are single parameter functions format :: Int -> String -> String myformat :: String -> String myformat = format 15 User String nameOf myformat formatedNameOfUser String nameOf format formatedNameOf
  • 19.
    Programming byWhole Values • Programming ByWordVsWhole Values •No word at a time processing • Work with entire value (data structures) • How to DoVsWhatTo Do • Focus on business logic implementation • e.g: No instructions on how to traverse a list for(int i=0; i< j; i++) { .... }
  • 20.
    Programming byWhole Values Do a map,filter and fold it! map (User ->String) -> List User -> List String ● List of Users -> List of User Names flatMap (User -> List String ) -> List User -> List String ● List of Users -> List of Phone Numbers filter (User->Boolean) -> List<User> -> List<User> ● Get all Admins from user list foldl ( Int -> User -> Int) -> Int -> List User -> Int ● Accumulate a value , say bill amount, from a list of all Users
  • 21.
    String Int User NormalWorld The Higher World List String Higher World Maybe Int List User User -> String List User Lift to the HigherWorld List.map (User -> String) List User -> List String Functors, Applicatives, Monads Functor Q
  • 22.
    String Int User NormalWorld The Higher World List String Higher World Maybe Int List User Transformation 1 Transformation N find :: String -> Maybe User How to get #phone numbers configured for the user? Option 1 Maybe User User Maybe Phones Phones Maybe Count Option 2 Maybe User Maybe Phones Maybe Count flatMap map
  • 23.
    Immutability Reference to adata structure is *only* for reading from the data structure Once you have a reference: • Guarantees that no one will modify its contents • Pass along to any number of threads without fear of ‘stepping on each other’ • Enables safe concurrency and parallelism • Any updates to the data structure returns a new reference
  • 24.
    Immutability Defects (holds listof open defects) val defectList = List(Defect(id="CSSid1234",age=48,engineer="jayas"),Defect(id="CSCid5678",age=12,engineer="otherone")) defectList.foreach { defect => if (defect.age > 28) { sendMsg(defect.engineer) } } defectList.dropWhile { defect => defect.engineer == "jayas" } Thread 1 Thread 2 val myDefectList =
  • 25.
    Functional Design Patterns Dependency Injection Strategy Pattern DecoratorPattern ... Function More on this is a separate talk! Q
  • 26.
    PureSystems AreUseless f f f f f f If no IOcan happen, no data can be modified , these collection of pure functions are useless! What is the way forward?
  • 27.
    List String Higher World MaybeInt List User Functional IO State Management IO (Network / Disk) All Impure operations are bounded in IO context , with the context composing with other contexts there by chaining operations together. IO / State Action IO/State Effect IO / State Value
  • 28.
  • 29.
  • 30.
    Design Principles in theCore 1 ParameterizeInputs String formatName(String) { if(name.length > maxLen) { return name.substring(maxLen).toLower() } else { return name.toLower() } } String formatName(String name , int maxLen) { if(name.length > maxLen) { return name.substring(maxLen).toLower() } else { return name.toLower() } } Do not keep global mutable state / vars
  • 31.
    Design Principles in theCore 2 UseComposition nameOf User String String format String User String nameOf format formatedNameOf
  • 32.
    Design Principles in theCore 3 FunctionsEverywhere Function as Parameter Function as return type f f :: Int -> Int -> String -> Int -> String( ) ( ) Int Int -> String Int -> String
  • 33.
    Design Principles in theCore 4 EnsureTotality (Use Types) String getAlias(User u ) { if(u….) { return u.XYZ } else { return null } } Optional<String > getAlias(User u) { return Optional.of(u.XYZ) } Q
  • 34.
    Design Principles in theCore 5 Compositionsin the Higher World IO String Higher World Maybe Int List User Context Effect / Value Context Effect / Value Context Effect / Value Use transformation functions for chaining operations together.
  • 35.
    EvolutionUse Case config Chat Router webhook API API fbm.js Fbm gateway WebChat Avoiding Global State Make 80% of Functions Pure
  • 36.
  • 37.
  • 38.
  • 39.
    ExternalWorld • User Actions •Timed Events • Browser Events Elm Runtime • Task Execution • Virtual DOM Model View Update Model Msg Cmd Msg Msg ApplicationElm Runtime Html Msg Model : Record Msg :Type Update : Msg -> Model - > (Model, Cmd Msg) View : Model -> Html Msg Elm Architecture
  • 40.
    DemoUseCase Notification Service Build anUI to send notifications Send Message Receive Message
  • 41.
  • 42.
    Micro Services Composition f1 f2 f3xy Use Case u1 u2 U3 Service req res System S1 S2 S4 S3 src sinkQ
  • 43.
    Events Application Event Sourcing Immutability Thoushall not change. Append Only Log Apache Kafka Writes Log Events stateReplay Events
  • 45.
    I am notsure.. WhatCan I do? Adopt these guidelines, if you are Java ( / C++ ) programmer • Isolate State management and IO to one part of the system • Let 80 % of the implementation be pure and stateless • Question and Revisit Impure functions / methods • Strictly adhere to Single Responsibility Principle of OO • Write pure functions unless it’s unavoidable • It’s fine to have a class with all static methods, which are pure! • Ensure there is only one and only one way a computation can happen
  • 46.
    I am notsure.. WhatCan I do Adopt these rules, if you are Java ( / C++) programmer • Use built-in functional constructs / third party libraries for the following • No nulls to indicate absence. Use Optional • Use it to indicate to other that data may or may not be present • No loops . • Use Functions in Collection libraries for working on collections • Use Immutable collections where available • Avoid shared memory / shared data • Follow message passing instead • Ensure only one thread acts on protected data, avoid locks
  • 47.
    Disclaimers • As Always,there are exceptions to all the rules , imperative may be best for performance critical parts of your system • Performance , Maintainability and Readability are of most importance • Do not compromise on these, especially when using the programming language natively does not support pure functional constructs • Design and Implementation of Immutable Data Structures are critical for data intensive operations
  • 48.
    Learn Functional Programming Haskell : https://github.com/data61/fp-course Haskellfor Greater Good : http://learnyouahaskell.com/chapters Elm : http://elm-lang.org/ Scala : http://docs.scala-lang.org/getting-started.html
  • 49.
  • 50.