intro to functional programming
TRANSCRIPT
Introduction to Functional
Programming
hello!Jordan Parmer
@ProfParmer
What is Functional Programming?
A Very Short Story
“
“We are builders first; technologists second
“The better builders we are, the more value we provide.
1.What is Functional
Programming?
What Does It Mean to be Imperative?
What Are The Most Common Problems In
Imperative OOP Code?
Null Pointer ExceptionsConcurrency Errors With Shared State
Broad Context of ReasoningDeep Inheritance Chains
Mutable State
What is the emphasis with imperative OOP
code?
Inheritance over composition
Abstraction by Inheritance
Internal class state and encapsulation
Imperative state mutations
Can We Do Better?
Return to Roots
“A functional programming language is a language that
emphasises programming with pure functions and immutable
data
Functional ProgrammingDeclarativeDescribe what, not howExpressions over statements
IdempotentImmutabilityFunctions are without side-effectsResults deterministic
Referentially TransparentAny expression can be replaced with the results of its value
AlgebraicTypes, equality, expressionsCategory theory
Functionally ComposableSeparate behavior from dataSmall functions composed together to make small building blocks
ParallelizableIdempotent code by nature enables local and distributed parallelization
Myths✘Requires a math degree to understand
✘Targets only math/science problems
✘Only relevant in academic circles
✘Not for “Line of Business” or “Form Over Data” apps
LIES!
Shocking Adjustments✘No Null✘Recursion over looping✘Avoidance of if-statements✘Map/filter/fold over iterating✘Monadic I/O✘Lots of foreign mathematical nomenclature✘No debuggers
“OOP makes code understandable by encapsulating moving parts.
FP makes code understandable by minimizing moving parts.
✘Michael Feathers
We Want These
You Don’t Need a Functional Language To
Be Functional
But It Does Help
Some [Subjective] ExamplesPure-ish
✘ Haskell✘ Erlang✘ Elm✘ Hope✘ Joy✘ Mercury✘ Scheme
Hybrids✘ Scala✘ F#✘ Kotlin✘ Swift✘ ML✘ LISP✘ Rust✘ Groovy✘ R✘ Dart✘ Ruby
Progressively Incorporating✘ Java 8✘ C#✘ Python✘ C++ 11
...and many more
Popular Languages
Beware Bias Ahead
JVM LanguagesScala
✘ Statically strong typed✘ FP with OOP good parts✘ Higher-order functions ✘ Insanely advanced type system✘ Currying, lazy-evaluation, pattern matching, etc.✘ Large commercial adoption
Twitter, Spotify, LinkedIn, Apache Spark, Kafka, etc.
✘ Backed by Lightbend + Scala Centre
Clojure✘ Dynamically typed✘ First-order functions✘ S-expressions✘ LISP-dialect
“Modern LISP that is symbiotic with Java”
.NET Languages
F#✘ Statically typed✘ Multi-paradigm: FP, imperative, OOP✘ .NET Framework✘ Visual Studio Tooling✘ Backed by Microsoft
Apple Languages
Swift✘ Scala...brought to you by Apple
I kid! I kid!
✘ FP alternative to Objective C✘ Borrowed some constructs from C & Objective
C✘ Optional types✘ Categories (i.e. type-classes)
Erlang Languages
Erlang✘ Ideal for distributed systems✘ Fault-tolerant, real-time✘ Highly-available systems✘ BEAM virtual machine
Elixir✘ Higher abstractions on top of Erlang
Reduces boilerplate✘ Runs on BEAM✘ New language but closely related to Erlang
Purist Languages
Haskell✘ Purely functional✘ Named after Haskell Curry✘ Strong type system✘ Introduced type-classes✘ Strict immutability
Elm✘ FP for the web✘ Graphical layout without destructive updates✘ Interops with (not compiles to)
JavaScript/CSS/HTML
...And So Many More
But What Does That All Mean?
Let’s Talk About Some FP Concepts
The SuspectsPure Functions
ExpressionsType Systems
Function ChainingMap, Filter, Fold
Functors, Monoids, Monads
Side-Effects Are The Source of Many Woes
Consider Algebra
f(x) = 3x2-7x+5Given the above
Find f(-4)
f(x) = 3x2-7x+5Given the above
Find f(-4)81
Function Has Side-Effects If It...✘Modifies a variable✘Modifies a data structure in place
e.g. append to linked list✘Throws an exception✘Prints to the console✘Writes to a file✘Updates a database✘Draws to the screen
Function Has Side-Effects If It...✘Modifies a variable✘Modifies a data structure in place
e.g. append to linked list✘Throws an exception✘Prints to the console✘Writes to a file✘Updates a database✘Draws to the screenHave you ever passed a
reference of a list to something else?
Single InputSingle OutputNo side effectsReferentially transparent Func
A B
Function Is Pure If It...
Why is this Good?
Functional Imperative OOP
Pure functions are easy to test
Local reasoning
In => OutVS
Objects are hard to test
Encapsulated state
Context
Global reasoning
Pure functions are easy to re-use
Low risk
Little-to-no dependencies
VSWe are terrible at making truly reusable objects
Challenges of tight coupling
Hard to abstract at correct level
Functional Imperative OOP
Pure functions are easy to parallelize
State in isolation
Just lego piecesVS
Sharing is cool in life...not in threads
How many concurrency patterns do we need to deal with shared state?
Functional Imperative OOP
Pure functions reduce need for most OOP design patterns VS
‘Cause who doesn’t want to know about your MVVMVMVMMVCCC singleton factory observer adapter thingy?
Functional Imperative OOP
Function Composition
Single Input Single Output
Func
A B
Functions Of This Sort Can Be Things
Func
A B
Functions Of This Sort Can Be Things
Func
A B
Called Functor
Two Functions
Func
Func
Compose Them Together
Func
Func
Compose Them Together
Func
Func
f(g(x))or
F o G x
New Function!
Func
Function Composition Is The New Old Injection
// Goal: Allow brewing coffee with different techniques and beans// Types// Beans => Grind// Grind => Coffee Compose to get: Beans => Coffee
trait Beanstrait Grindtrait Coffee
case class ColumbianBeans() extends Beans
def brew(grind: Beans => Grind)(brew: Grind => Coffee): Beans => Coffee = { brew compose grind}
def burrGrind(beans: Beans): Grind = ???def blendGrind(beans: Beans): Grind = ???
def pourOver(grind: Grind): Coffee = ???def drip(grind: Grind): Coffee = ???def frenchPress(grind: Grind): Coffee = ???
val pourOverWithBurrGrind = brew(burrGrind)(pourOver)val myFavoriteBeans = ColumbianBeans()val myFavoriteCoffee = pourOverWithBurrGrind(myFavoriteBeans)
// TIME TO WAKE UP!!!
// Goal: Allow brewing coffee with different techniques and beans// Types// Beans => Grind// Grind => Coffee Compose to get: Beans => Coffee
trait Beanstrait Grindtrait Coffee
case class ColumbianBeans() extends Beans
def brew(grind: Beans => Grind)(brew: Grind => Coffee): Beans => Coffee = { brew compose grind}
def burrGrind(beans: Beans): Grind = ???def blendGrind(beans: Beans): Grind = ???
def pourOver(grind: Grind): Coffee = ???def drip(grind: Grind): Coffee = ???def frenchPress(grind: Grind): Coffee = ???
val pourOverWithBurrGrind = brew(burrGrind)(pourOver)val myFavoriteBeans = ColumbianBeans()val myFavoriteCoffee = pourOverWithBurrGrind(myFavoriteBeans)
// TIME TO WAKE UP!!!
brew is a higher order function
Why Useful?
// Imagine how many combinations we can use this code// Imagine how easy testing is
object Person { def drink(coffee: Coffee): AwakePerson = ???}
def wakeSomeone(makeIt: () => Coffee): AwakePerson = { Person.drink(makeIt())}
Anything that makes coffee
Expressions
// Which right triangle that has integers for all sides and all sides equal to// or smaller than 10 has a perimeter of 24?
def rightTriangle: Seq[Int] = for { c <- 1 to 10 b <- 1 to c a <- 1 to b if ((Math.pow(a,2) + Math.pow(b,2) == Math.pow(c,2)) && (a + b + c == 24)) } yield (a, b, c)
rightTriangle
Answer: [6, 8, 10] It’s Not About the How but the What
Type Constraints
f(x) = 3/xInt => Double
f(x) = 3/x1 => 3/1 => 3.02 => 3/2 => 1.50 => 3/0 => ???
f(x) = 3/x1 => 3/1 => 3.02 => 3/2 => 1.50 => 3/0 => ???
AH, PICKLES!
f(x) = 3/x1 => 3/1 => 3.02 => 3/2 => 1.50 => 3/0 => ???
THROW AN EXCEPTION?
f(x) = 3/xInt => Double
0 => 3/0 => ???
THROW AN EXCEPTION?
THEN THIS IS A LIE!
f(x) = 3/xInt => Double
0 => 3/0 => ???
THROW AN EXCEPTION?
THEN THIS IS A LIE!
Are You A Liar?
But Functions Are Types!
Types Don’t Have to Be Open to All
Circumstances
f(x) = 3/xNonZeroInt => Double
Just Became Constrained And Self-Documented
f(x) = 3/x Zero Not Allowed BY NonZeroInt
-1 => 3/-1 => -31 => 3/1 => 3
2 => 3/2 => 1.5
Static Types Just Became Enforced
Domain Documentation
We Sometimes Refer to Our Programs as an
“Algebra”
If It Compiles, It Probably Works
Function Chaining
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Yeah, I know this is bad.
But you know you’ve
seen this before.
Hang with me.
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Pyramid of Doom
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Pyramid of Doom
The Only Lines Doing
Work
That’s only 20%!
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Pyramid of Doom
Look at all those nulls!
Half this code is null checking.
Nulls are a serious code
smell!
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Pyramid of Doom
Look at all those nulls!
Half this code is null checking.
Nulls are a serious code
smell!
Null Pointer Exceptions Waiting to Happen
def foo(): Thing = { val x = DoAThing() if (x != null) { val y = DoAnotherThing(x) if (y != null) { val z = DoYetAnotherThing(y) if (z != null) { return z } else { return null } } else { return null } } else { return null }}
Pyramid of Doom
Throw in async operations, we can
create the same pyramid with
nested callbacks.
FP to the Rescue!
def foo(): Option[Int] = { for { x <- DoAThing() y <- DoAnotherThing(x) z <- DoYetAnotherThing(y) } yield z}
def foo(): Option[Int] = { for { x <- DoAThing() y <- DoAnotherThing(x) z <- DoYetAnotherThing(y) } yield z}
Every line is significant
def foo(): Option[Int] = { for { x <- DoAThing() y <- DoAnotherThing(x) z <- DoYetAnotherThing(y) } yield z} Boom!
Every line is significant
Code Examples
Application Becomes a Collection of Pure
Functions
Just Fold the Collection
ParallelizationFold All the Pieces!
MonadsThe Key to a Lot of Awesome
And confusion
Functors, Monoids, and Bears
Functor
A => B
Monoid
Type AIdentity Function
Binary Combinator
Monad
Type with a Functor + FlatMap
State
State
State stays in box
State
The box is a “monad”
State
Think of monad as
collection of at most one
item
State
You can do collection things to monadsOperation creates new monad of same type but new state .map(...)
.flatMap(...)
.fold(...)
.filter(...)
.collect(...)
.head
State
If Monad is “empty”, applied functions are ignored
.map(...)
.flatMap(...)
.fold(...)
.filter(...)
.collect(...)
.head
def foo(): Option[Int] = Some(100)
val myFoo = foo()
val result = myFoo .map(i => i + 100) .map(i => i.toString) .map(i => i + " is a bigger number") .getOrElse("didn't do anything")
// result = "200 is a bigger number"
def foo(): Option[Int] = None
val myFoo = foo()
val result = myFoo .map(i => i + 100) .map(i => i.toString) .map(i => i + " is a bigger number") .getOrElse("didn't do anything")
// result = "didn’t do anything"
Executed but not applied
Monads are so much more but let’s keep it
simple for now…
...another talk to come
2.
Common Applications
Trends in Functional Programming
Data Processing Streaming Parallelizatio
n
Machine Learning
Heavy Computation Attracting
Talent
3.
Disadvantages?
4.
Final Thoughts
Natural way to migrate To Functional
Programming?
thanks!Any questions?
You can find me at@ProfParmer