Functional Programming
Patterns
Andrey Denisov, Paragonex Team
Practices ?
OO-pattern/principle
• Factory pattern
• Strategy pattern
• Decorator pattern
• Facade pattern
• Single Responsibility principle
FP equivalent
• Functions
• Functions
• Functions
• Functions
• Functions
Immutability
Pure functions
Partial application/Currying
Function composition
Functional dependency injection
class Point {
var x = 0.0
var y = 0.0
init(x : Double, y : Double) {
self.x = x
self.y = y
}
func scale (factor : Double) {
self.x = self.x * factor
self.y = self.y * factor
}
}
var p = Point(x: 1.0, y: 1.0)
var q = p
q.scale(factor: 5)
p.scale(factor: 3) // 3.0 3.0 ?
print(p.x, p.y) // 15.0 15.0 WTF?!!???
Not predictable
Not thread-safe
func scaling(point:Point, by factor:Double) -> Point {
return Point(x: point.x * factor, y: point.y * factor)
}
p = scaling(point: p, by: 5)
q = scaling(point : q, by: 3)
print(p.x, p.y) // 5.0 5.0
Immutability
Pure functions
Partial application/currying
Function composition
Functional dependency injection
Must not have observable side-effects
Same inputs yield same result
// Pure : concat two strings and return
// new string
func + (lhs:String, rhs:String) -> String
// Impure : advances to the next element
// and return it
class Generator {
func next() -> Element
}
Pure functions are easy to reason about
Reasoning about code that might not be inpure :
customer.set(name:newName)
var name = customer.getName()
// Is customeer being changed ?
let newCustomer = set(customer:customer,newName :name)
Customer being changed
let name = self.getName(for customer:newCustomer)
More practical benefits of pure functions :
• Cacheable (memoization)
• same answer every time
• No order dependency
• I can evaluate them in every order I like
• Parallelizable
Immutability
Pure functions
Partial application/Currying
Function composition
Functional dependency injection
func setUserPWD(id:String,
pwd:String,
dbConnection:Connection) -> Result {
let statement = dbConnection.prepareStatememt(for: pwd, id:id)
let result = statement.execute()
return result
}
main()
level1
level3
level4
level5
needs connection
func setUserPWD(id:String,
pwd:String) -> (Connection -> Result) {
return { connection in
let statement = connection.prepareStatememt(for: pwd, id:id)
let result = statement.execute()
return result
}
}
let applyConnection = setUserPWD(id:userID, pwd:userPwd)
/// in some place:
let connection = DataManager.connection
let result = applyConnection(connection)
Let’s apply currying :
Partial application :
// getLine : (Double,UIColor, LineStyle) -> Line
func getLine(width:Double,
color:UIColor,
style :LineStyle) -> Line
thinSolidLine : (UIColor) -> Line
let thinSolidLine = { color in
getLine(width:1.0, color:color, style : .solid)
}
Immutability
Pure functions
Partial application/Currying
Function composition
Functional dependency injection
Applying filter to image (functional wrapper
around an existing, object-oriented API)
Core Image API
Key class : CIFilter
Example of using :
let parameters = [
kCIInputImageKey: image
]
let filter = CIFilter(name: "CIGaussianBlur", withInputParameters:
parameters)
let outputImage = filter.outputImage()
FP approach :
typealias Filter = CIImage -> CIImage
func blur(radius: Double) -> Filter {
return { image in
let parameters = [
kCIInputRadiusKey: radius,
kCIInputImageKey: image
]
let filter = CIFilter(name: "CIGaussianBlur",
withInputParameters: parameters
let outputImage = filter.outputImage
return outputImage
}
}
blur filter :
basic type :
typealias Filter = CIImage -> CIImage
// blur filter
func blur(radius: Double) -> Filter { … }
// vignette filter
func vignette(intensity: NSNumber,radius:Double) -> Filter {
return { image in
let parameters = [
kCIInputRadiusKey : radius,
kCIInputIntensityKey: intensity,
kCIInputImageKey: image
]
let filter = CIFilter(name: "CIVignette",
withInputParameters: parameters)
let outputImage = filter.outputImage
return outputImage
}
}
let blurRadius = 5.0
let intensity = 3.0
let vignetteRadius = 4.0
let image = CIImage(contentsOfURL: url)
let blurredImage = blur(blurRadius)(image)
let result = vignette(intensity:intensity,
radius:vignetteRadius)(blurredImage)
Let’s do something more ..
// f(x) and g(x)
// composeFilters => f(g(x))
func composeFilters(filter1: Filter, _ filter2: Filter) -> Filter {
return { image in filter2(filter1(image)) }
}
let myNewFilter = composeFilters(blur(blurRadius),
vignette(intensity:intensity,
radius:vignetteRadius))
let result = myNewFilter(image)
And more ..
let myNewFilter = blur(blurRadius) >>> vignette(intensity:intensity,
radius:vignetteRadius)
let result = myFilter(image)
infix operator >>> { associativity left }
func >>> (filter1: Filter, filter2: Filter) -> Filter {
return { image in filter2(filter1(image)) }
}
Immutability
Pure functions
Partial application/Currying
Function composition
Functional dependency injection
Task :
- update user profile
- send notification
Problem :
- Breaked Interface Segregation Principle (Unneeded
interface methods)
class UserProfileUpdateService {
let dbService : IDBService
let emailService : IEmailService
init(dbService:IDBService, emailService:IEmailService) {
self.dbService = dbService
self.emailService = emailService
}
func updateProfile(user:User,someInfo:Info) {
let currentEmail = self.dbService.getEmail(user.id)
self.dbService.updateProfile(user.id, user.name, someInfo)
emailService.sendEmailNotification(to : currentEmail,
with: someInfo)
...
}
}
typealias UpdateProfile : (UserID, UserName, Info) -> ()
typealias NotifyUser : (Email,Info) -> ()
typealias GetEmail : (UserID) -> (Email)
class UserProfileUpdateService {
func updateProfile(user:User,
someInfo:Info,
getEmail : GetEmail
updateProfile: UpdateProfile,
notify : NotifyUser) {
let currentEmail = getEmail(user.id)
updateProfile(user.id, user.name, info)
notify(currentEmail, someInfo)
...
}
}
// In method call :
let dbService = IDBService()
let emailService = IEmailService()
userUpdateService.updateProfile(user:someUser,
info : someInfo,
getEmail: dbService.getEmail,
updateProfile : dbService.updateProfile,
notify: emailService.sendEmailNotification)
Useful links :
http://fsharpforfunandprofit.com
https://www.lektorium.tv/course/22779
http://functionaltalks.org

Functional programming principles

  • 1.
  • 3.
    OO-pattern/principle • Factory pattern •Strategy pattern • Decorator pattern • Facade pattern • Single Responsibility principle FP equivalent • Functions • Functions • Functions • Functions • Functions
  • 5.
    Immutability Pure functions Partial application/Currying Functioncomposition Functional dependency injection
  • 6.
    class Point { varx = 0.0 var y = 0.0 init(x : Double, y : Double) { self.x = x self.y = y } func scale (factor : Double) { self.x = self.x * factor self.y = self.y * factor } }
  • 7.
    var p =Point(x: 1.0, y: 1.0) var q = p q.scale(factor: 5) p.scale(factor: 3) // 3.0 3.0 ? print(p.x, p.y) // 15.0 15.0 WTF?!!??? Not predictable Not thread-safe
  • 9.
    func scaling(point:Point, byfactor:Double) -> Point { return Point(x: point.x * factor, y: point.y * factor) } p = scaling(point: p, by: 5) q = scaling(point : q, by: 3) print(p.x, p.y) // 5.0 5.0
  • 10.
    Immutability Pure functions Partial application/currying Functioncomposition Functional dependency injection
  • 11.
    Must not haveobservable side-effects Same inputs yield same result
  • 12.
    // Pure :concat two strings and return // new string func + (lhs:String, rhs:String) -> String // Impure : advances to the next element // and return it class Generator { func next() -> Element }
  • 13.
    Pure functions areeasy to reason about Reasoning about code that might not be inpure : customer.set(name:newName) var name = customer.getName() // Is customeer being changed ? let newCustomer = set(customer:customer,newName :name) Customer being changed let name = self.getName(for customer:newCustomer)
  • 15.
    More practical benefitsof pure functions : • Cacheable (memoization) • same answer every time • No order dependency • I can evaluate them in every order I like • Parallelizable
  • 16.
    Immutability Pure functions Partial application/Currying Functioncomposition Functional dependency injection
  • 17.
    func setUserPWD(id:String, pwd:String, dbConnection:Connection) ->Result { let statement = dbConnection.prepareStatememt(for: pwd, id:id) let result = statement.execute() return result }
  • 18.
  • 19.
    func setUserPWD(id:String, pwd:String) ->(Connection -> Result) { return { connection in let statement = connection.prepareStatememt(for: pwd, id:id) let result = statement.execute() return result } } let applyConnection = setUserPWD(id:userID, pwd:userPwd) /// in some place: let connection = DataManager.connection let result = applyConnection(connection) Let’s apply currying :
  • 21.
    Partial application : //getLine : (Double,UIColor, LineStyle) -> Line func getLine(width:Double, color:UIColor, style :LineStyle) -> Line thinSolidLine : (UIColor) -> Line let thinSolidLine = { color in getLine(width:1.0, color:color, style : .solid) }
  • 22.
    Immutability Pure functions Partial application/Currying Functioncomposition Functional dependency injection
  • 23.
    Applying filter toimage (functional wrapper around an existing, object-oriented API) Core Image API Key class : CIFilter Example of using : let parameters = [ kCIInputImageKey: image ] let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters) let outputImage = filter.outputImage()
  • 24.
    FP approach : typealiasFilter = CIImage -> CIImage func blur(radius: Double) -> Filter { return { image in let parameters = [ kCIInputRadiusKey: radius, kCIInputImageKey: image ] let filter = CIFilter(name: "CIGaussianBlur", withInputParameters: parameters let outputImage = filter.outputImage return outputImage } } blur filter : basic type :
  • 25.
    typealias Filter =CIImage -> CIImage // blur filter func blur(radius: Double) -> Filter { … } // vignette filter func vignette(intensity: NSNumber,radius:Double) -> Filter { return { image in let parameters = [ kCIInputRadiusKey : radius, kCIInputIntensityKey: intensity, kCIInputImageKey: image ] let filter = CIFilter(name: "CIVignette", withInputParameters: parameters) let outputImage = filter.outputImage return outputImage } }
  • 26.
    let blurRadius =5.0 let intensity = 3.0 let vignetteRadius = 4.0 let image = CIImage(contentsOfURL: url) let blurredImage = blur(blurRadius)(image) let result = vignette(intensity:intensity, radius:vignetteRadius)(blurredImage) Let’s do something more ..
  • 28.
    // f(x) andg(x) // composeFilters => f(g(x)) func composeFilters(filter1: Filter, _ filter2: Filter) -> Filter { return { image in filter2(filter1(image)) } } let myNewFilter = composeFilters(blur(blurRadius), vignette(intensity:intensity, radius:vignetteRadius)) let result = myNewFilter(image)
  • 29.
    And more .. letmyNewFilter = blur(blurRadius) >>> vignette(intensity:intensity, radius:vignetteRadius) let result = myFilter(image) infix operator >>> { associativity left } func >>> (filter1: Filter, filter2: Filter) -> Filter { return { image in filter2(filter1(image)) } }
  • 30.
    Immutability Pure functions Partial application/Currying Functioncomposition Functional dependency injection
  • 31.
    Task : - updateuser profile - send notification
  • 32.
    Problem : - BreakedInterface Segregation Principle (Unneeded interface methods) class UserProfileUpdateService { let dbService : IDBService let emailService : IEmailService init(dbService:IDBService, emailService:IEmailService) { self.dbService = dbService self.emailService = emailService } func updateProfile(user:User,someInfo:Info) { let currentEmail = self.dbService.getEmail(user.id) self.dbService.updateProfile(user.id, user.name, someInfo) emailService.sendEmailNotification(to : currentEmail, with: someInfo) ... } }
  • 33.
    typealias UpdateProfile :(UserID, UserName, Info) -> () typealias NotifyUser : (Email,Info) -> () typealias GetEmail : (UserID) -> (Email) class UserProfileUpdateService { func updateProfile(user:User, someInfo:Info, getEmail : GetEmail updateProfile: UpdateProfile, notify : NotifyUser) { let currentEmail = getEmail(user.id) updateProfile(user.id, user.name, info) notify(currentEmail, someInfo) ... } } // In method call : let dbService = IDBService() let emailService = IEmailService() userUpdateService.updateProfile(user:someUser, info : someInfo, getEmail: dbService.getEmail, updateProfile : dbService.updateProfile, notify: emailService.sendEmailNotification)
  • 35.