Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
0% found this document useful (0 votes)
4 views

Functional Programming

Uploaded by

John Irving
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
4 views

Functional Programming

Uploaded by

John Irving
Copyright
© © All Rights Reserved
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 6

Functional programming revolves around composing functions rather than setting and

updating variables.

Functional programming is about declaring WHAT you want to happen, rather than HOW
you want it to happen.

Imperative/procedural programming declares what should happen, and also exactly HOW
it happens.

ex) Imperative:
num = get_a()
num = transform_a(num)
num = transform_b(num)
return num

functional:
return transform_b(transform_a(get_a()))

in functional programming, values need not be mutated.

Functional programming is stateless- there is no memory of the past. Every


action is executed as if it were being done for the first time.

The Inheritance principle of OOP conflicts with Functional programming's


focus on functions.

Immutability is a core tenat of functional programming- once a value has been


created, it cannot be changed.
This is advantageous because immutable data is easier to reason about. You
can just look at it and know what it is.

Lists are mutable, while tuples are immutable. You can append to a list, but
you cannot append to a tuple. You are able to concatenate tuples together to form a
new tuple.
You may assign this new tuple to an old tuple's variable name, but understand
you aren't changing the old tuple, you're reassigning the variable name.

Functional programming is declarative. When using functional programming, we


declare what we want the computer to do rather than giving step-by-step
implementations

ex) The code-as-infrastrcture toole Terraform is declarative- you


declare what cloud server resources you are going to use and the tool will try to
create it, without the exact steps needed to get there.

Scripting is about being imperative, you must write out each step you want the
program to take.

Neither classes nor functions are superior to one another- consider the pros and
cons of each when choosing an approach. However, when conflicted, default to
functions.

Classes encourage you to think about the world as a collection of objects.


Objects bundle behavior, data, and state together in a way that draws
boundaries between instances of things (like chess pieces on a board).

Functions encourage you to think about the world as a series of transformations on


data.
Functions take data as input, transform it, and return it as output. For
example, a function might take the state of a chess board and a move as input, and
return the new state of the board as output.

Anonymous or "lambda" functions are functions without a name.


ex) lambda x: x + 1
This is a function that takes a single arguemnt (x) and returns it with a
value of x + 1
This function can be assigned to a variable and be called like anyother
function.
Essentialy it allows functions as values.

First class functions are just normal functions.

Higher order functions are functions that accepts another function as an argument
or returns a function:
Below is an example of a first-class example.
ex) def square(x):
return x * x
# Assign function to variable
f = square

print(f(5))
Below is an example of a Higher-order function:
ex) def square(x):
return x * x
def my_map(func, arg_list):
result = []
for i in arg_list:
result.append(func(i))
return result

squares = my_map(square, [1, 2, 3, 4, 5])


print (square)
#prints [1, 4, 9, 16, 25]

Pure functions are functions which always return the same value(s) given the same
arguments (deterministic), and whose evaluation has no side effects.
They do not change the external state of the program (like changing variables
outside of their scope), and they do
ex) def findMax(nums):
max_val = float('-inf')
for num in nums:
if max_val < num:
max_val = num
return max_val

NB: While functional programing excludes mutable variables, pure functions do


not, and so may contain loops.

Compare an impure function:


# instead of returning a value
# this function modifies a global variable
global_max = float('-inf')

def findMax(nums):
global global_max
for num in nums:
if global_max < num:
global_max = num

Reference vs Value:
Pass by reference: a function can mutate the original value that was passed
into it
pass by value: A function can NOT mutate the orignal value that was passed
into it, it gets a copy

In python these are passed by reference:


Lists, Dictionaries, sets

These are passed by value:


Integers, Floats, Strings, Booleans, Tuples

ex) Passing by value(immutable)


def attempt_to_modify(num):
num += 1

my_num = 1
#my_num = 1
attempt_to_modify(my_num)
#my_num = 1

ex) Passing by reference(mutable)


def modify_list(lst):
lst.append(4)
#my_list = [1, 2 , 3, 4]
my_list = [1, 2, 3]
modify_list(my_list)
#my_list = [1, 2, 3, 4]

Input/Output (i/o) or "side effects" is undesirable in functional programming.


Usually this refers to the parts of program that interacts with the outside
world.
ex) Reading/writing from a file on the hardrive
Making a network request across the internet
reading/writing from a database
printing to the console

While i/o is inevitable, seek to reduce it as much as possible.

Memoization: It is a specific type of caching, when we cache the result of a


computation so it doesn't need to be computed again.

A recursive function without a base case will loop infinitely, eventually resulting
in a crash.
A base case is an if-statement that causes the loop to end.
ex) def print_chars(word, i):
if i == len(word):
return
print(word[i])
print_chars(word, i + 1)

print_chars("Hello", 0)

The if-statement above causes the function to exit the loop when i equals the
length of the word variable.
Recursion over a tree of dictionaries:

def list_files(current_node, current_path=""):


file_paths = []
for n in current_node:
node_values = current_node[n]
if node_values is None:
file_paths.append(current_path + "/" + n)
else:
file_paths.extend(list_files(node_values,
current_path + "/" + n))

return file_paths

Imperative coding (i.e. using loops) is not a feature of functional program.


Aim to limit their use.

Function Transformation: a higher-order function takes a function(s) as input and


returns a new function.

ex) def multiply(x, y):


return x * y

def add(x, y):


return x + y

def self_math(math_func):
def inner_func(x):
return math_func(x, x)
return inner_func

square_func = self_math(multiply)
double_func = self_math(add)

print(square_func(5))
# prints 25

print(double_func(5))
# prints 10

the self_math function takes two functions as its argument and returns a new
function

Function closures allow function to "remember" the values of variable(s) within

remember to mark variables you want to use outside the function they're defined in
with nonlocal:
ex) def concatter():
doc = ""
def inner_func(word):
# "nonlocal" tells Python to use the doc
# variable from the enclosing scope
nonlocal doc
doc += word + " "
return doc
return inner_func
Currying is a type of function transformationthat takes multiple arguments into a
sequence of functions, each with a single argument.
ex) def sum(a, b):
return a + b

print(sum(1, 2))
# prints 3
Can be curried into:
def sum(a):
def inner_sum(b):
return a + b
return inner_sum

print(sum(1)(2))
# prints 3

You will typically only curry functions when you need to satisfy a specific
function signature, for example, an outside database requires it.

*args and **kwargs: *args collects positional arguments into a tuple.


**kwargs collects keyword (named) arguments into a dictionary.

def print_arguments(*args, **kwargs):


print(f"Positional arguments: {args}")
print(f"Keyword arguments: {kwargs}")

print_arguments("hello", "world", a=1, b=2)


# Positional arguments: ('hello', 'world')
# Keyword arguments: {'a': 1, 'b': 2}

a decorator is just Pythonic syntactic sugar for function transformations (and


sometimes closures).
"Syntactic sugar" just means "a more convenient syntax".

A sum type is a useful tool provided by functional programming. It is the opposite


of a product type, a product type being made up of multiple instances
of other types. Examples of product types include tuples, dictionaries, and
classes.

ex) person = {
"is_fat": True,
"is_tall": False
}
the total number of combination the person class can have is four, the
product of 2x2

A dictionary of 3 booleans would have a total of 8 possible values.

Sum types however can only be one of several other types. For example, a
single boolean is a sum type
because it can only be either True or False.
Python does not support sum types, but it can mimic them by manually checking
for and handling invalid values.

Enums: a way to represent fixed sets of values; a kind of sum type. Not enforced in
any meaningful way.

from enum import Enum


class Color(Enum):
RED = 1
GREEN = 2
BLUE = 3

def color_to_hex(color):
if color == Color.RED:
return "#FF0000"
if color == Color.GREEN:
return "#00FF00"
if color == Color.BLUE:
return "#0000FF"
raise Exception("Unknown color")

You might also like