In computer science, functional programming is a programming paradigm—a style of building the structure and elements of computer programs—that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data. It is a declarative programming paradigm, which means programming is done with expressions or declarations instead of statements.
- Wikipedia
Once you understand it, you can't explain it!
Except for that you can't really understand this!
Lambda
Lambda Calculus
Lazy evaluation
Monoid
Monad
Comonad
Applicative Functor
Morphism
Endomorphism
Isomorphism
Setoid
SSE
3+ years of experience
Javascript, React, React Native
C# F# SQL Server
Microsoft Azure
GraphQL
Mathematical functions
Immutability
Functions are "things"
Types are cheap
Currying
Partial application
Composition
Function signatures
Patterns
let add1 x = x + 1
val add1 : int -> int
Pure functions
Output never changes
No side-effects
Exactly one input and one output
It's not that you can't change values, it's the way you do it.
Values and types are immutable by default
You "transform" the state rather than reassigning
Why?
Separation of logic and data
Helps reduce side-effects
Previous and new state can be compared
Increased predictability
for (int i=0; i<array.Count-1; i++) {
array[i] = array[i] * 2
}
let data = [1;2;3;4;5]
let data2 = List.map (fun x -> x * 2) data
With mutation
When done the right way -
type PersonalName = {FirstName:string; LastName:string}
// immutable person
let john = {FirstName="John"; LastName="Doe"}
// new person
let alice = {john with FirstName="Alice"}
Functions are just like values
They can be passed around
Functions can return functions
Functions can be stored as values
Functions have their own type
Function calls always return something
// (int -> int -> int) -> int -> int -> int ....(1)
let binaryOperator operation num1 num2 =
operation num1 num2
// int -> int -> int
let add num1 num2 = num1 + num2
let sub num1 num2 = num1 - num2
// int -> int -> int
let addNums = binaryOperator add
let subNums = binaryOperator sub
let a = addNums 1 2 // 3
let b = subNums 3 4 // 1
FP encourages creating lots and lots of small types
Small types form together bigger types
Most suitable for Domain driven design (DDD)
Types in F#
Function types, The unit type, Tuples, Records, Discriminated Unions, Option types, Lists
type PersonalName = {FirstName:string; LastName:string}
// Addresses
type StreetAddress = {Line1:string; Line2:string; Line3:string }
type ZipCode = ZipCode of string
type StateAbbrev = StateAbbrev of string
type ZipAndState = {State:StateAbbrev; Zip:ZipCode }
type USAddress = {Street:StreetAddress; Region:ZipAndState}
type UKPostCode = PostCode of string
type UKAddress = {Street:StreetAddress; Region:UKPostCode}
type InternationalAddress = {
Street:StreetAddress; Region:string; CountryName:string}
// choice type -- must be one of these three specific types
type Address = USAddress | UKAddress | InternationalAddress
// Email
type Email = Email of string
// Phone
type CountryPrefix = Prefix of int
type Phone = {CountryPrefix:CountryPrefix; LocalNumber:string}
type Contact =
{
PersonalName: PersonalName;
// "option" means it might be missing
Address: Address option;
Email: Email option;
Phone: Phone option;
}
Designing world with types
Making illegal states unrepresentable
Eg. Shopping cart - Paid, Active, Empty
Breaking multi-parameter functions into smaller one-parameter functions
F# functions default to this mode
//normal version
let printTwoParameters x y =
printfn "x=%i y=%i" x y
//explicitly curried version
let printTwoParameters x = // only one parameter!
let subFunction y =
printfn "x=%i y=%i" x y // new function with one param
subFunction // return the subfunction
Baking-in some of the parameters of a function
Fire at the right moment
m1 and m2 are masses of objects
G is the universal gravitational constant
r is the distance between objects
// Regardless of the planet
let calculateForce m1 m2 r =
(G * m1 * m2) / r
// Specific to earth
let calculateForceWithEarth = calculateForce (5.972 * 10^24)
Building new functions from existing ones
Being able to call a chain of functions just once
let negate num = num * -1
let add1 num = num + 1
let sub1 num = num - 1
let mul100 num = num * 100
let composedFunc = negate >> add1 >> sub1 >> mul100
let result = composedFunc 1 // -100
var result = negate(add1(sub1(mul100(1))));
C# version
A function signature can give you some idea of what it does
val testA = int -> int
val testB = int -> int -> int
val testC = int -> (int -> int)
val testD = (int -> int) -> int
val testE = int -> int -> int -> int
val testF = (int -> int) -> (int -> int)
val testG = int -> (int -> int) -> int
val testH = (int -> int -> int) -> int
Making code more readable
Making code easier to reason about
Making code easier to test
Make our users happy
Substantial investment of time and effort
Steep learning curve
Mathematical vocabulary
Simple things get complicated Eg. Updating array
Functional core
Object oriented shell