After a long time learning and working with object-oriented programming, I took a step back to think about system complexity.
"Complexity is anything that makes software hard to understand or to modify.
" — John Outerhout
Doing some research, I found functional programming concepts like immutability and pure function. Those concepts are big advantages to build side-effect-free functions, so it is easier to maintain systems — with some other benefits.
In this post, I will tell you more about functional programming, and some important concepts, with a lot of code examples.
This article uses Clojure as a programming language example to explain Functional Programming. If you are not comfortable with a LISP-type-of-language, I also published the same post in JavaScript. Take a look: Functional Programming Principles in Javascript
What is functional programming?
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 — Wikipedia
Pure functions
The first fundamental concept we learn when we want to understand functional programming is pure functions. But what does that really mean? What makes a function pure?
So how do we know if a function is pure
or not? Here is a very strict definition of purity:
- It returns the same result if given the same arguments (it is also referred as
deterministic
) - It does not cause any observable side effects
It returns the same result if given the same arguments
Imagine we want to implement a function that calculates the area of a circle. An impure function would receive radius
as the parameter, and then calculate radius * radius * PI
. In Clojure, the operator comes first, so radius * radius * PI
becomes (* radius radius PI)
:
Why is this an impure function? Simply because it uses a global object that was not passed as a parameter to the function.
Now imagine some mathematicians argue that the PI
value is actually 42
and change the value of the global object.
Our impure function will now result in 10 * 10 * 42
= 4200
. For the same parameter (radius = 10
), we have a different result. Let's fix it!
TA-DA ?! Now we’ll always pass thePI
value as a parameter to the function. So now we are just accessing parameters passed to the function. No external object.
- For the parameters
radius = 10
&PI = 3.14
, we will always have the same the result:314.0
- For the parameters
radius = 10
&PI = 42
, we will always have the same the result:4200
Reading Files
If our function reads external files, it’s not a pure function — the file’s contents can change.
Random number generation
Any function that relies on a random number generator cannot be pure.
It does not cause any observable side effects
Examples of observable side effects include modifying a global object or a parameter passed by reference.
Now we want to implement a function to receive an integer value and return the value increased by 1.
We have the counter
value. Our impure function receives that value and re-assigns the counter with the value increased by 1.
Observation: mutability is discouraged in functional programming.
We are modifying the global object. But how would we make it pure
? Just return the value increased by 1. Simple as that.
See that our pure function increase-counter
returns 2, but the counter
value is still the same. The function returns the incremented value without altering the value of the variable.
If we follow these two simple rules, it gets easier to understand our programs. Now every function is isolated and unable to impact other parts of our system.
Pure functions are stable, consistent, and predictable. Given the same parameters, pure functions will always return the same result. We don’t need to think of situations when the same parameter has different results — because it will never happen.
Pure functions benefits
The code’s definitely easier to test. We don’t need to mock anything. So we can unit test pure functions with different contexts:
- Given a parameter
A
→ expect the function to return valueB
- Given a parameter
C
→ expect the function to return valueD
A simple example would be a function to receive a collection of numbers and expect it to increment each element of this collection.
We receive the numbers
collection, use map
with the inc
function to increment each number, and return a new list of incremented numbers.
For the input
[1 2 3 4 5]
, the expected output
would be [2 3 4 5 6]
.
Immutability
Unchanging over time or unable to be changed.
When data is immutable, its state cannot change after it’s created. If you want to change an immutable object, you can’t. Instead, you create a new object with the new value.
In Javascript we commonly use the for
loop. This next for
statement has some mutable variables.
For each iteration, we are changing the i
and the sumOfValue
state. But how do we handle mutability in iteration? Recursion! Back to Clojure!
So here we have the sum
function that receives a vector of numerical values. The recur
jumps back into the loop
until we get the vector empty (our recursion base case
). For each "iteration" we will add the value to the total
accumulator.
With recursion, we keep our variables immutable.
Observation: Yes! We can use reduce
to implement this function. We will see this in the Higher Order Functions
topic.
It is also very common to build up the final state of an object. Imagine we have a string, and we want to transform this string into a url slug
.
In OOP in Ruby, we would create a class, let’s say, UrlSlugify
. And this class will have a slugify!
method to transform the string input into a url slug
.
Beautiful! It’s implemented! Here we have imperative programming saying exactly what we want to do in each slugify
process — first lower case, then remove useless white spaces and, finally, replace remaining white spaces with hyphens.
But we are mutating the input state in this process.
We can handle this mutation by doing function composition, or function chaining. In other words, the result of a function will be used as an input for the next function, without modifying the original input string.
Here we have:
trim
: removes whitespace from both ends of a stringlower-case
: converts the string to all lower-casereplace
: replaces all instances of match with replacement in a given string
We combine all three functions and we can "slugify"
our string.
Speaking of combining functions, we can use the comp
function to compose all three functions. Let's take a look:
Referential transparency
Let’s implement a square function
:
This (pure) function will always have the same output, given the same input.
Passing “2” as a parameter of the square function
will always returns 4. So now we can replace the (square 2)
with 4. That's it! Our function is referentially transparent
.
Basically, if a function consistently yields the same result for the same input, it is referentially transparent.
pure functions + immutable data = referential transparency
With this concept, a cool thing we can do is to memoize the function. Imagine we have this function:
The (+ 5 8)
equals 13
. This function will always result in 13
. So we can do this:
And this expression will always result in 16
. We can replace the entire expression with a numerical constant and memoize it.
Functions as first-class entities
The idea of functions as first-class entities is that functions are also treated as values and used as data.
In Clojure it’s common to use defn
to define functions, but this is just syntactic sugar for (def foo (fn ...))
. fn
returns the function itself. defn
returns a var
which points to a function object.
Functions as first-class entities can:
- refer to it from constants and variables
- pass it as a parameter to other functions
- return it as result from other functions
The idea is to treat functions as values and pass functions like data. This way we can combine different functions to create new functions with new behavior.
Imagine we have a function that sums two values and then doubles the value. Something like this:
Now a function that subtracts values and the returns the double:
These functions have similar logic, but the difference is the operators functions. If we can treat functions as values and pass these as arguments, we can build a function that receives the operator function and use it inside our function. Let’s build it!
Done! Now we have an f
argument, and use it to process a
and b
. We passed the +
and -
functions to compose with the double-operator
function and create a new behavior.
Higher-order functions
When we talk about higher-order functions, we mean a function that either:
- takes one or more functions as arguments, or
- returns a function as its result
The double-operator
function we implemented above is a higher-order function because it takes an operator function as an argument and uses it.
You’ve probably already heard about filter
, map
, and reduce
. Let's take a look at these.
Filter
Given a collection, we want to filter by an attribute. The filter function expects a true
or false
value to determine if the element should or should not be included in the result collection. Basically, if the callback expression is true
, the filter function will include the element in the result collection. Otherwise, it will not.
A simple example is when we have a collection of integers and we want only the even numbers.
Imperative approach
An imperative way to do it with Javascript is to:
- create an empty vector
evenNumbers
- iterate over the
numbers
vector - push the even numbers to the
evenNumbers
vector
We can use the filter
higher order function to receive the even?
function, and return a list of even numbers:
One interesting problem I solved on Hacker Rank FP Path was the Filter Array problem. The problem idea is to filter a given array of integers and output only those values that are less than a specified value X
.
An imperative Javascript solution to this problem is something like:
We say exactly what our function needs to do — iterate over the collection, compare the collection current item with x
, and push this element to the resultArray
if it pass the condition.
Declarative approach
But we want a more declarative way to solve this problem, and using the filter
higher order function as well.
A declarative Clojure solution would be something like this:
This syntax seems a bit strange in the first place, but is easy to understand.
#(> x
%) is just a anonymous function that receive
s x and compares it with each element in the collection
. % represents the parameter of the anonymous function — in this case the current element inside the fil
ter.
We can also do this with maps. Imagine we have a map of people with their name
and age
. And we want to filter only people over a specified value of age, in this example people who are more than 21 years old.
Summary of code:
- we have a list of people (with
name
andage
). - we have the anonymous function
#(< 21 (:age
%)). Remember that th
e % represents the current element from the collection? Well, the element of the collection is a people map. If wedo (:age {:name "TK" :age 2
6}), it returns the age value,
26 in this case. - we filter all people based on this anonymous function.
Map
The idea of map is to transform a collection.
The map
method transforms a collection by applying a function to all of its elements and building a new collection from the returned values.
Let’s get the same people
collection above. We don't want to filter by “over age” now. We just want a list of strings, something like TK is 26 years old
. So the final string might be :name is :age years old
where :name
and :age
are attributes from each element in the people
collection.
In a imperative Javascript way, it would be:
In a declarative Clojure way, it would be:
The whole idea is to transform a given collection into a new collection.
Another interesting Hacker Rank problem was the update list problem. We just want to update the values of a given collection with their absolute values.
For example, the input [1 2 3 -4 5]
needs the output to be [1 2 3 4 5]
. The absolute value of -4
is 4
.
A simple solution would be an in-place update for each collection value.
We use the Math.abs
function to transform the value into its absolute value, and do the in-place update.
This is not a functional way to implement this solution.
First, we learned about immutability. We know how immutability is important to make our functions more consistent and predictable. The idea is to build a new collection with all absolute values.
Second, why not use map
here to "transform" all data?
My first idea was to build a to-absolute
function to handle only one value.
If it is negative, we want to transform it in a positive value (the absolute value). Otherwise, we don’t need to transform it.
Now that we know how to do absolute
for one value, we can use this function to pass as an argument to the map
function. Do you remember that a higher order function
can receive a function as an argument and use it? Yes, map can do it!
Wow. So beautiful! ?
Reduce
The idea of reduce is to receive a function and a collection, and return a value created by combining the items.
A common example people talk about is to get the total amount of an order. Imagine you were at a shopping website. You’ve added Product 1
, Product 2
, Product 3
, and Product 4
to your shopping cart (order). Now we want to calculate the total amount of the shopping cart.
In imperative way, we would iterate the order list and sum each product amount to the total amount.
Using reduce
, we can build a function to handle the amount sum
and pass it as an argument to the reduce
function.
Here we have shopping-cart
, the function sum-amount
that receives the current total-amount
, and the current-product
object to sum
them.
The get-total-amount
function is used to reduce
the shopping-cart
by using the sum-amount
and starting from 0
.
Another way to get the total amount is to compose map
and reduce
. What do I mean by that? We can use map
to transform the shopping-cart
into a collection of amount
values, and then just use the reduce
function with +
function.
The get-amount
receives the product object and returns only the amount
value. So what we have here is [10 30 20 60]
. And then the reduce
combines all items by adding up. Beautiful!
We took a look at how each higher-order function works. I want to show you an example of how we can compose all three functions in a simple example.
Talking about shopping cart
, imagine we have this list of products in our order:
We want the total amount of all books in our shopping cart. Simple as that. The algorithm?
- filter by book type
- transform the shopping cart into a collection of amount using map
- combine all items by adding them up with reduce
Done! ?
Resources
I’ve organised some resources I read and studied. I’m sharing the ones that I found really interesting. For more resources, visit my Functional Programming Github repository.
Intros
- Learning FP in JS
- Intro do FP with Python
- Overview of FP
- A quick intro to functional JS
- What is FP?
- Functional Programming Jargon
Pure functions
Immutable data
- Immutable DS for functional programming
- Why shared mutable state is the root of all evil
- Structural Sharing in Clojure: Part 1
- Structural Sharing in Clojure: Part 2
- Structural Sharing in Clojure: Part 3
- Structural Sharing in Clojure: Final part
Higher-order functions
- Eloquent JS: Higher Order Functions
- Fun fun function Filter
- Fun fun function Map
- Fun fun function Basic Reduce
- Fun fun function Advanced Reduce
- Clojure Higher Order Functions
- Purely Function Filter
- Purely Functional Map
- Purely Functional Reduce
Declarative Programming
That’s it!
Hey people, I hope you had fun reading this post, and I hope you learned a lot here! This was my attempt to share what I’m learning.
Here is the repository with all codes from this article.
Come learn with me. I’m sharing resources and my code in this Learning Functional Programming repository.
I hope you saw something useful to you here. And see you next time! :)
TK.
FAQs
What is the basic principle of functional programming? ›
Functional programming is a programming paradigm which has its roots in mathematics, primarily evolved from lambda calculus.It is a declarative type of programming style. Its main focus is on “what to solve” in contrast to an imperative style where the main focus is “how to solve”.
Is it hard to learn functional programming? ›Functional programming is a new skill and you'll feel like a newbie programmer as you learn it. However, it's just like any new skill– it's always difficult to understand from the onset, but as you learn the rules, you'll start to get more comfortable.
Can you do functional programming in C++? ›The paradigms commonly associated with C++ include procedural, object-oriented and generic programming. Because C++ provides excellent tools for high-level programming, even functional-style programming is quite reasonable.
Do I have to learn functional programming? ›Easy to maintain
The code tends to be more concise and more predictable. With simpler, cleaner code, it's also easier to test and to maintain. Taking advantage of techniques like higher order functions, functional composition, and currying helps make code more reusable. And code reuse can result in fewer lines of code.
The four principles of object-oriented programming (abstraction, inheritance, encapsulation, and polymorphism) are features that - if used properly - can help us write more testable, flexible, and maintainable code.
What are the 5 basic principle of programming? ›- Write programs for people, not computers.
- Define things once and only once.
- Use a version control system.
- Optimize software only after it works correctly.
- Be a good code citizen.
- Keep it short.
- Language-specific style guides.
Haskell. The language is named after a mathematician and is usually described to be one of the hardest programming languages to learn. It is a completely functional language built on lambda calculus.
Is functional programming a math? ›Because functional programming relies on careful selection of types and transformation between types, FP can be perceived as more "mathematical".
How long does it realistically take to learn to code? ›It typically takes 6-12 months to learn to code on your own. Likewise, a bachelor's degree in computer science or computer programming usually takes four years.
Does anyone use functional programming? ›Many programming languages and dialects use the functional paradigm, including Scheme, Common Lisp (CL), and Elixir. Many of today's top programming languages—including C#, Java, JavaScript, PHP, and Python—support programming in a functional style or use features found in FP.
Which language is best for functional programming? ›
- Clojure.
- Elixir.
- Haskell.
- Scala.
- Python.
- Elm.
- F#
- Erlang.
Functional programming is typically used to programme symbolic computation and list processing applications. Functions exist in all higher-order coding languages, but pure FPLs only use the functional paradigm.
Is Python functional programming or not? ›Python is not a strictly functional programming language. But it is trivial to write Python in a functional style. There are three basic functions on iterables that allow us to write a powerful program in a very trivial way: filter, map, and reduce.
Is Python considered a functional language? ›Although Python is not primarily a functional language, it's good to be familiar with lambda , map() , filter() , and reduce() because they can help you write concise, high-level, parallelizable code. You'll also see them in code that others have written.
Does functional programming have a future? ›Still, "functional programming is unmistakably the future," says Felix. "It can help make a large proportion of the current problems we have in software development disappear.” And it can even be used within established Java ecosystems.
What are the three 3 fundamentals of programming? ›- sequence is the order in which instructions occur and are processed.
- selection determines which path a program takes when it is running.
- iteration is the repeated execution of a section of code when a program is running.
KISS is a term also used in developing solutions to programming problems. Literally translated, KISS means “keep it simple, stupid” or “keep it stupid simple“. a simple design makes service and maintenance child's play.
What is the kiss principle in programming? ›Keep it simple, stupid (KISS) is a design principle which states that designs and/or systems should be as simple as possible. Wherever possible, complexity should be avoided in a system—as simplicity guarantees the greatest levels of user acceptance and interaction.
What are the 7 common principles? ›Humanity, impartiality, neutrality, independence, voluntary service, unity and universality: these seven Fundamental Principles sum up the Movement's ethics and are at the core of its approach to helping people in need during armed conflict, natural disasters and other emergencies.
What is basic programming knowledge? ›Your basic programming skills should, at least, include:. • Writing and compiling a program. • Debugging programs. • Input/Output using files. • Use of variables, control structures and loops.
What is the simplest programming language ever? ›
If you're new to the world of programming, Python is the best coding language to start with. It's a beginner-friendly code with a simplified syntax, using fewer lines than other coding languages, making it easy to read, understand, and use.
What is the hottest programming language right now? ›...
1. Javascript.
Functional programming has different skills that you can mix and match and use them together with other paradigm skills.
What level of math is programming? ›Pre-Algebra for Programming
Since programming languages are algebraic, they use variables, functions, and operations to define and solve problems. The good news is that you probably learned pre-algebra in middle or high school.
- Haskell.
- SML.
- Clojure.
- Scala.
- Erlang.
- Clean.
- F#
- ML/OCaml Lisp / Scheme.
Are You Too Old to Learn Programming? Let's get this out of the way: no, you are not too old to program. There isn't an age limit on learning to code, and there never was. But all too often, insecurity and uncertainty compel older adults to put a ceiling on their achievement potential.
What age do most programmers learn to code? ›Software developers' average age when writing their first line of code worldwide 2021. The majority of software developers surveyed for this study wrote their first line of code between 11 and 17 years old. However, 14 percent of respondents wrote their first coding line between five to ten years old.
Can the average person learn to code? ›Almost anyone can learn to code. The challenge is finding the right resources. edX offers bootcamps and free online courses for computer programming, computer science, and a wealth of subfields such as data science, machine learning, and more.
What companies use functional programming? ›Functional Programming is used in demanding industries like eCommerce or streaming services in companies such as Zalando, Netflix, or Airbnb. Developers that work with Functional Programming languages are among the highest paid in the business.
What are the disadvantages of functional programming? ›- There is no efficient purely functional unsorted dictionary or set. ...
- There is no purely functional weak hash table. ...
- There are no purely functional concurrent collections. ...
- Most graph algorithms look worse and run much slower when written in an FP style.
Which functional programming language has the most jobs? ›
- Elixir.
- Kotlin.
- Swift.
- Scala.
- Rust.
Functional programming has historically been less popular than imperative programming, but many functional languages are seeing use today in industry and education, including Common Lisp, Scheme, Clojure, Wolfram Language, Racket, Erlang, Elixir, OCaml, Haskell, and F#.
Why is functional programming not more popular? ›Functional programming is less popular because no major successful platform has adopted a functional language as the preferred language. I don't buy that functional languages are unpopular because they are unintuitive. Losts of stuff in JavaScript is highly unintuitive.
Which functional programming language is beginner friendly? ›- Elixir.
- Erlang.
- Common Lisp.
- Haskell.
- F#
- Clojure.
- Elm.
- Racket.
It improves modularity. It allows us to implement lambda calculus in our program to solve complex problems. Some programming languages support nested functions which improve maintainability of the code. It reduces complex problems into simple pieces.
Is functional programming better for machine learning? ›Functional programming uses logical functions, dealing with expressions rather than statements. Developers agree on the fact that a programming model that focuses on mathematical functions and logic is well suited to machine learning.
Is functional programming just recursion? ›One common and powerful functional programming technique is called recursion. Recursion is simply a function that calls itself. In this lesson, we will talk about how we can use recursion instead of loops such as forEach or for . Knowing how to write a recursive function is an extremely important part of coding.
Is functional programming better than OOP Python? ›With a language like Python, it is almost entirely up to the programmer as to what paradigm they use primarily. OOP is currently the paradigm used by most developers, mostly for its accessibility. Functional programming is great for the server side of applications, data manipulation, and web crawling.
Is JavaScript more functional than Python? ›Python web development responds slower than JavaScript web development, but JavaScript web apps code better, function quicker and manage a lot more data. Due to the asynchronous programming capabilities of the Node. js frameworks, which provide high-end scalability to the apps, JavaScript excels in this area.
Is functional programming used in data science? ›Most functional languages have “ statistical” in the title. That's convenient because a data scientist is a lot like a statistician, just with programming and machine-learning skills tacked on. Functional languages can often be faster, and most of all easier for a data scientist.
What level of language is Python? ›
Python is an object-oriented, high-level programming language. Object-oriented means this language is based around objects (such as data) rather than functions, and high-level means it's easy for humans to understand.
Is machine learning a functional programming language? ›Secondly, machine learning — and deep learning in particular — is functional by design. Given the right ecosystem, there are several compelling reasons to perform deep learning in an entirely functional manner: Deep learning models are compositional.
Is it good to learn functional programming? ›Easy to maintain
The code tends to be more concise and more predictable. With simpler, cleaner code, it's also easier to test and to maintain. Taking advantage of techniques like higher order functions, functional composition, and currying helps make code more reusable. And code reuse can result in fewer lines of code.
With an imperative approach, a developer writes code that specifies the steps that the computer must take to accomplish the goal. This is sometimes referred to as algorithmic programming. In contrast, a functional approach involves composing the problem as a set of functions to be executed.
What is the best language to learn functional programming? ›- Clojure: A general-purpose programming language, compatible with JVM and avoids duplication during calls to Java. ...
- Elixir: ...
- Haskell: ...
- Scala: ...
- Python: ...
- Elm: ...
- F#: ...
- Erlang:
Some programming languages support nested functions – This significantly improves the maintainability of the code. Problems are easier to pinpoint and solve – FP's reliance on pure functions makes debugging and unit testing easier. Pure functions also prevent confusing issues and errors from developing in the code.
Does learning functional programming make you a better programmer? ›It makes you a better programmer
Unlike in imperative languages, in functional languages, variables are immutable by default, and do not depend on the state of the program. Along with referencial transparency (given the same inputs, a function always return the same results), this increases consistency.
Big tech companies like Facebook, Twitter, Amazon, and Paypal use functional languages like Erlang, Scala, Haskell, and Clojure. Walmart, Staples, and Monsanto have Clojure divisions. Google, Target, Intel, and Microsoft use Haskell. These giant companies are not letting functional languages go anywhere.
Which programming language is in demand 2023? ›- Javascript.
- Python.
- Go.
- Java.
- Kotlin.
- PHP.
- C#
- Swift.
If you're new to the world of programming, Python is the best coding language to start with. It's a beginner-friendly code with a simplified syntax, using fewer lines than other coding languages, making it easy to read, understand, and use.
Is JavaScript is functional programming? ›
Is JavaScript a functional programming language or object-oriented? Thanks to new developments in ES6, we can say that JavaScript is both a functional as well as object-oriented programming language because of the various first-class features it provides.
Is functional programming coming back? ›Object-oriented and imperative programming aren't going away, but functional programming is finding its way into more codebases.
How can I be good at functional programming? ›- 1) Avoid libraries that do it all for you. ...
- 2) Use languages designed for compositional programming. ...
- 3) Use something to keep you consistent. ...
- 4) Remember that functions map sets of values to sets of values. ...
- 5) Think about the properties of your functions.
Most functional languages have “ statistical” in the title. That's convenient because a data scientist is a lot like a statistician, just with programming and machine-learning skills tacked on. Functional languages can often be faster, and most of all easier for a data scientist.