Functional Programming
(Using Python)
Software Engineer at Pesto Tech
@salman_arfat
Presented at PyDelhi
Life is too short for imperative programming
-- John Hughes (One of the designers of Haskell)
A little bit of History!
Main Programming Paradigms
A paradigm describes distinct concepts or thought patterns some scientific discipline.
- Imperative Programming
- Functional Programming
- Logic Programming
- Object Oriented Programming
Imperative programming
- modifying mutable variables
- using assignments
- and control structures such as if-else, loops, break, continue, return
uses statements that change a program's state.
The most common way (informal) way to understand imperative programs is as instruction sequences to a von Neumann Computer
Given a numeric string, get the next character that comes after it.
There's a strong correspondence between
- Mutable variables ≈ memory cells
- Variable dereferences ≈ load instructions
- Variable Assignments ≈ Store instructions
- Control Structures ≈ Jumps
Problem: How can we avoid conceptualising programs words by word?
Functional Programming
treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
It's a paradigm of programming where fundamental operation is the application of functions to arguments.
- In restricted sense, FP is programming without mutable variables, assignments, loops, and other imperative control structures
- In a wider sense, focusing on functions
- Functions are values that can be produced, consumed, and composed.
Example: QuickSort
class QuickSort
{
int partition(int arr[], int low, int high)
{
int pivot = arr[high];
int i = (low-1); // index of smaller element
for (int j=low; j<high; j++)
{
if (arr[j] <= pivot)
{
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i+1;
}
void sort(int arr[], int low, int high)
{
if (low < high)
{
int pi = partition(arr, low, high);
sort(arr, low, pi-1);
sort(arr, pi+1, high);
}
}
}
FP combines the flexibility and power of abstract mathematics with the intuitive clarity of abstract mathematics.
-- xkcd
Why is it Useful?
There are many possible answers to this question, but generally speaking:
- The abstract nature of functional programming leads to considerably simpler programs;
- It also supports a number of powerful new ways to structure and reason about programs.
total = 0;
for (i = 1; i <= 10; ++i)
total = total+i;
sum [1..10]
What is a function?
A function is a
-
relation
-
between input values
-
to output values
Functional Programs have
- Pure Functions
- Immutability
- No Side effects
- First Class Functions
- Higher Order Functions
- Function Composition
- and much more ...
Side Effects
A side effect is any application state change that is observable outside the called function other than its return value. Side effects include:
- Modifying any external variable or object property (e.g., a global variable, or a variable in the parent function scope chain)
- Logging to the console
- Writing to the screen
- Writing to a file
- Writing to the network
- Triggering any external process
- Calling any other functions with side-effects
Stateless programs
- Idempotent (pure) functions
- Order of evaluation not defined
- Lazy evaluation possible
- Optimizations
- Concurrent processing
- Easier to test and debug
- Side effects can’t be eliminated, but can be isolated
Higher Order Functions
A function is called higher-order if it takes a function as an argument or returns a function as a result.
The Map Function
The higher-order library function called map applies a function to every element of a list.
map :: (a -> b) -> [a] -> [b]
> map (+1) [1,3,5,7]
[2,4,6,8]
Reduce
def nextCharFromNumberString(numStr):
trimmed = numStr.strip()
number = int(trimmed)
number = number + 1
return chr(number)
result = nextCharFromNumberString(' 64 ')
print(result)
def nextCharFromNumberString2(numStr):
return map(
chr,
map(
lambda num: num + 1,
map(
lambda stripped: int(stripped),
map(
lambda string: string.strip(),
[numStr]
)
)
)
)
Example
- We have a dict of colors with their hex values.
- We want to remove the hash sign
- and turn the string to uppercase.
def findColor(name):
colors = {
'red': '#ff4444',
'blue': '#3b5998',
'yellow': '#fff68f'
}
return colors.get(name)
Left or Right
Either = Right || Left
Enforce a null check with composable code branching using Either
Either doesn't actually come into play. It will just refer to one of these two types.
Usually, we don't know if we have a Right or a Left. We don't know which one it is.
class Left:
def __init__(self, x):
self.x = x
def map(self, f):
return Left(self.x)
def fold(self, f, _):
return f(self.x)
def __str__(self):
return 'Left(%s)' % self.x
class Right:
def __init__(self, x):
self.x = x
def map(self, f):
return Right(f(self.x))
def fold(self, _, g):
return g(self.x)
def __str__(self):
return 'Right(%s)' % self.x
Functional Programming in Python
By Arfat Salman
Functional Programming in Python
- 451