Functional programming (FP) is the process of building software by composing pure functions. Nowadays, employers are looking for programmers who can draw on multiple paradigms to solve problems. Functional programming especially is gaining in popularity due to its efficiency and scalability to solve modern problems.
But how can you make the jump from OOP to FP?
Today, we’ll explore the core concepts of functional programming and show you how to implement them in Python, JavaScript, and Java!
Here’s what we’ll cover today:
This course teaches the basics of functional programming, how it’s used, and how to use it in Python. By the end, you’ll have a new programming paradigm to add under your belt.
Learn Functional Programming in Python
Functional programming is a declarative programming paradigm where programs are created by applying sequential functions rather than statements.
Each function takes in an input value and returns a consistent output value without altering or being affected by the program state.
These functions complete a single operation and can be composed in sequence to complete complex operations. The functional paradigm results in highly modular code, since functions can be reused across the program and can be called, passed as parameters, or returned.
Pure Functions produce no side effects and do not depend on global variables or states.
Functional programming is used when solutions are easily expressed in functions and have little physical meaning. While object-oriented programs model code after real-world objects, functional programming excels at mathematical functions where intermediate or end values have no physical correlation.
Some common uses of functional programming are AI design, ML classification algorithms, financial programs, or advanced mathematical function models.
Simplified: Functional programs execute many pure, single-purpose functions in sequence to solve complex mathematical or non-physical problems.
Not all programming languages support functional programming. Some languages, like Haskell, are designed to be functional programming languages., Other languages, like JavaScript, have functional capabilities and OOP capabilities, and others do not support functional programming at all.
Haskell: This is the clear favorite language for functional programming. It’s memory safe, has excellent garbage collection, and fast due to early machine code compiling. Haskell’s rich and static typing system gives you access to unique algebraic and polymorphic types that make functional programming more efficient and easier to read.
Erlang: This language and descendent, Elixir, have established a niche as the best functional language for concurrent systems. While not as popular or widely usable as Haskell, it’s often used for backend programming. Erlang has more recently gained traction for scalable messaging apps like Whatsapp and Discord.
Clojure: This language is a functional-first dialect of Lisp used on the Java virtual machine (JVM). It’s a predominantly functional language that supports both mutable and immutable data structures but is less strictly functional than others here. If you like Lisp, you’ll like Clojure.
F#: F# is similar to Haskell (they’re in the same language group), but has less advanced features. It also has minor support for object-oriented constructions.
Functional programs are designed with a few core concepts in mind.
The core building blocks of a functional program are variables and functions rather than objects and methods. You should avoid global variables because mutable global variables make the program hard to understand and lead to impure functions.
Pure functions have two properties:
Side effects are caused if a function alters the program state, overwrites an input variable, or in general makes any change along with generating an output. Pure functions are less buggy because side effects complicate a program’s state.
Referential transparency means that any function output should be replaceable with its value without changing the result of the program. This concept ensures that you create functions that only complete a single operation and achieve a consistent output.
Referential transparency is only possible if the function does not affect the program state or generally attempts to accomplish more than one operation.
Immutable data or states cannot be changed once set and allow a stable environment for a function’s output to be constant. It’s best practice to program each function to produce the same result regardless of the program state. If it does rely on a state, the state must be immutable to ensure that the function output remains constant.
Functional programming approaches generally avoid shared state functions (multiple functions relying on the same state) and mutating state functions (function relies on a mutable function) because they make programs less modular. If you must use shared state functions, make it an immutable state.
One major difference between object-oriented programming and functional programming is that functional programs avoid constructions like If-Else
statements or loops that can create different outputs on each execution.
Functional programs use recursion in place of loops for all iteration tasks.
Functions in functional programming are treated as a data type and can be used like any other value. For example, we populate an array with functions, pass them as parameters, or store them in variables.
Higher-order functions can accept other functions as parameters or return functions as output. Higher-order functions allow us greater flexibility in how we make function calls and abstract over actions.
Functions can be sequentially executed to complete complex operations. The result of each function is passed to the next function as an argument. This allows you to call a series of functions with just a single function call.
Learn to use Python to build your own functional programs for free. Educative’s skimmable courses are text-based and designed to get you hands-on experience fast.
Python has partial support for functional programming as a multi-paradigm language. Some Python solutions of mathematical programs can be more easily accomplished with a functional approach.
The most difficult shift to make when you start using a functional approach is to cut down how many classes you use. Classes in Python have mutable attributes which make it difficult to create pure, immutable functions.
Try to instead keep most of your code at the module level and only switch to classes if you need to.
Let’s see how to achieve pure, immutable functions and first-class functions in Python. Then, we’ll learn the syntax for composing functions.
Many of Python’s built-in data structures are immutable by default:
Tuples are especially useful as an immutable form of an array.
# Python code to test that
# tuples are immutable
tuple1 = (0, 1, 2, 3)
tuple1[0] = 4
print(tuple1)
This code causes an error because it attempts to reassign an immutable tuple object. Functional Python programs should use these immutable data structures often to achieve pure functions.
The following is a pure function because it has no side effects and will always return the same output:
def add_1(x):
return x + 1
Functions are treated as objects in Python. Here’s our quick guide on how you can use functions in Python:
Functions as objects
def shout(text):
return text.upper()
Pass function as parameter
def shout(text):
return text.upper()
def greet(func):
# storing the function in a variable
greeting = func("Hi, I am created by a function passed as an argument.")
print greeting
greet(shout)
Return function from another function
def create_adder(x):
def adder(y):
return x+y
return adder
To compose functions in Python, we’ll use a lambda function
call. This allows us to call any number of arguments in a single call.
import functools
def compose(*functions):
def compose2(f, g):
return lambda x: f(g(x))
return functools.reduce(compose2, functions, lambda x: x)
At line 4, we’ll define a function compose2
that takes two functions as arguments f
and g
.
At line 5, we return a new function that represents the composition of f
and g
.
Finally, at line 6, we return the results of our composition function.
JavaScript has long offered functional capabilities due to its support for first-class functions. Functional programming has recently become more popular in JavaScript because it boosts performance when used in frameworks like Angular and React.
Let’s take a look at how to achieve different functional concepts using JavaScript. We’ll focus on how to create the core concepts; pure functions, first-class functions, and function compositions.
To start creating pure functions in JavaScript we’ll have to use functional alternatives of common behavior, like const
, concat
, and filter()
.
The let
keyword sets a mutable variable. Declaring with const
instead guarantees that the variable is immutable because it prevents reassignment.
const heightRequirement = 46;
function canRide (height){
return height >= heightRequirement;
}
We also need to use functional alternatives to manipulate arrays. The push()
method is the usual way to append an element onto an array. Unfortunately, push()
modifies the original array and is therefore impure.
Instead we’ll use the functional equivalent, concat()
. This method returns a new array that contains all original elements as well as the newly added element, The original array is not modified when using concat()
.
const a = [1, 2]
const b = [1, 2].concat(3)
To remove an item from an array, we’d usually use the pop()
and slice()
methods. However, these are not functional as they modify the original array. Instead, we’ll use filter()
that creates a new array that contains all elements that pass a conditional test.
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
JavaScript supports first-class functions by default. Here’s a quick guide of what we can do with functions in JavaScript.
Assign function to variable
const f = (m) => console.log(m)
f('Test')
Add function to array
const a = [
m => console.log(m)
]
a[0]('Test')
Pass function as argument
const f = (m) => () => console.log(m)
const f2 = (f3) => f3()
f2(f('Test'))
Return function from another function
const createF = () => {
return (m) => console.log(m)
}
const f = createF()
f('Test')
In JavaScript, we can compose functions with chained function calls:
obj.doSomething()
.doSomethingElse()
Alternatively, we can pass a function execution into the next function:
obj.doSomething(doThis())
If we want to compose more functions we can instead use lodash
to simplify the composition. Specifically, we’ll use the compose
feature that is given an argument and then a list of functions.
The first function in the list uses the original argument as its input. Later functions inherit an input argument from the return value of the function before it.
import { compose } from 'lodash/fp'
const slugify = compose(
encodeURIComponent,
join('-'),
map(toLowerCase),
split(' ')
)
slufigy('Hello World') // hello-world
Java does not truly support functional programming as Python or JavaScript does. However, we can mimic functional programming behavior in Java by using lambda functions, streams, and anonymous classes.
Ultimately, the Java compiler was not created with functional programming in mind and therefore cannot receive many of the benefits of functional programming.
Several of Java’s built-in data structures are immutable:
You can also create your own immutable classes with the final
keyword.
// An immutable class
public final class Student
{
final String name;
final int regNo;
public Student(String name, int regNo)
{
this.name = name;
this.regNo = regNo;
}
public String getName()
{
return name;
}
public int getRegNo()
{
return regNo;
}
}
The final
keyword on the class prevents the construction of a child class. The final
on name
and regNo
make it impossible to change the values after object construction.
This class also has a parameterized constructor, getter methods for all variables, and no setter methods which each help to make this an immutable class.
Java can use lambda functions to achieve first-class functions. Lambda takes in a list of expressions like a method but does not need a name or to be predefined.
We can use lambda expressions in place of functions as they are treated as standard class objects that can be passed or returned.
// FIRST-CLASS
Supplier<String> lambda = myObject::toString;
// HIGHER-ORDER
Supplier<String> higherOrder(Supplier<String> fn) {
String result = fn.get();
return () -> result;
}
Java contains an interface, java.util.function.Function
, that gives methods for functional composition. The compose
method executes the passed function first (multiplyByTen
) then passes the return to the external function (square
).
The andThen
method executes the external function first and then the function within its parameters.
Function<Integer, Integer> square = (input) -> input * input;
Function<Integer, Integer> multiplyByTen = (input) -> input * 10;
// COMPOSE: argument will be run first
Function<Integer, Integer> multiplyByTenAndSquare = square.compose(multiplyByTen);
// ANDTHEN: argument will run last
Function<Integer, Integer> squareAndMultiplyByTen = square.andThen(multiplyByTen);
At lines 1 and 2, we first create two functions, square
and multiplyByTen
.
Next at lines 5 and 8, we make 2 composite functions multiplyByTenAndSquare
and squareAndMultiplyByTen
that each take two arguments (to satisfy square
).
These composite functions each complete both original functions but in different orders. You can now call the composite functions to execute both original functions on the same input.
Today, we went over some general functional programming concepts and explored how those core concepts appear in Python, JavaScript, and Java.
To help you learn Python for functional programming, Educative has created the course Learn Functional Programming in Python. This course starts from scratch and gives you everything you need to know to get writing your own programs fast. By the end, you’ll have covered all of Python’s unique functional programming features.
Happy learning!
Free Resources