functional programming principles
TRANSCRIPT
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
ImmutabilityPure 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 predictableNot 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 functionsPartial 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/CurryingFunction 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.connectionlet result = applyConnection(connection)
Let’s apply currying :
Partial application :
// getLine : (Double,UIColor, LineStyle) -> Linefunc getLine(width:Double, color:UIColor, style :LineStyle) -> Line
thinSolidLine : (UIColor) -> Linelet thinSolidLine = { color in getLine(width:1.0, color:color, style : .solid)}
Immutability
Pure functions
Partial application/Currying
Function compositionFunctional dependency injection
Applying filter to image (functional wrapper around an existing, object-oriented API)
Core Image APIKey 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 filterfunc blur(radius: Double) -> Filter { … }
// vignette filterfunc 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.0let intensity = 3.0let 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