A brief introduction to functional programming.
Even if slides present some simple Python code, functional programming patterns applies to other languages too.
1. Introduction to functional programming in
Python
Francesco Bruni
@brunifrancesco
Original notebook @ https://github.com/brunifrancesco/cds_talk
2. Who am I
• MSc Student in Telecommunication Engineering @ Poliba (Italy)
• Despite a Java background, I prefer Python whenever possible
• Data science addicted
3. What we'll talk about
• Why functional programming
• Why Python
• Functional features in Python
▪ First class functions
▪ Immutable vs mutable data
▪ Lazy evaluation
▪ Recursion vs loop
▪ (Manual) Currying
▪ Monads
• Useful functional programming resources
• Conclusions
4. Why functional programming
• Think in terms of functions
• State as function evaluation, not variables assignment
• Testing is (more) easy
• A new viewpoint is required
5. import random
def sum_imperative():
res = 0
for n in [random.random() for _ in range(100)]:
res += n
return res
def sum_functional():
return sum(random.random() for _ in range(100))
7. Pros
• Mature enough
• Multiparadigm and multipurpose
• Easy to learn
• Interactive shell
• Multiple implementantions (C, Java, Python, .NET, Ruby, Js)
• Extending requires few lines of code
8. Cons
• Python 3 vs Python 2
• No typed variables
• CPython is the widest used implementation
• Changing implementation/version is not (always) a free iussues transition
9. Functional features in Python
• Not all functional patterns apply to Python
• Hybrid approach is often requested
• Other libraries needs to be (eventually) integrated
10. First class functions
• Functions are objects too
• They have fields too and some basic methods
12. High order functions
• Functions which accept functions as params;
• Functions which return other functions;
• Python std lib examples for mapping: filter, map, sorted,
• Python std lib examples for reducing: max, min, sum
Task: given the previous generated list, filter some number out and sort the result by the
second digit.
13. def filter_out():
result = python_better_approach()
exclude = map(lambda item: item ** 2, range(30))
result = filter(lambda item: item not in exclude, result)
return sorted(result, key=lambda item: str(item)[1])
filter(lambda item: item not in map(lambda item: item ** 2, range(30)),
python_better_approach(size=100))
14. Class for function composition
Handle class creation via a new class syntax, allowing a "static" configuration
15. from collections.abc import Callable
class ExcludeFunction(Callable):
def __init__(self, exclude_function):
self.exclude_function= exclude_function
def __call__(self, value):
return None if not value else self.exclude_function(value)
result = python_better_approach(size=100)
exclude_function_1 = lambda item: item ** 2
ex_func = ExcludeFunction(exclude_function_1)
result = filter(lambda item: not item == ex_func(item), result)
assert(sorted(result, key=lambda item: str(item)[1]) == filter_out())
16. Immutable vs mutable data structure
• Since state is not given by variable assignement, there's no need of mutables data
• (Almost) no classes are required
• Instead of lists, tuples are reccomanded
• Instead of classes, namedtuple can be used
17. from collections.abc import Callable
class ExcludeFunction(Callable):
def __init__(self, exclude_function):
self.exclude_function= exclude_function
def __call__(self, value):
return None if not value else self.exclude_function(value)
result = python_better_approach(size=100)
exclude_function_1 = lambda item: item ** 2
ex_func = ExcludeFunction(exclude_function_1)
result = filter(lambda item: not item == ex_func(item), result)
assert(sorted(result, key=lambda item: str(item)[1]) == filter_out())
18. Strict vs not strict evaluation
• Strict evaluation requires that all operators needs to be evaluated
• Non strict (or lazy) evaluation, evaluates expression if and when requested
19. Use case: Python iterables structures
• Iterable: objects than can be iterated;
• Iterator: objects than can be iterated and consumed only once, with two main problems:
▪ They track their state in a stateful object
▪ Their values are generated a priori
▪ They aren't lazy
• Generators: lazy evaluated data structures:
▪ They track state in function, instead of object
▪ They don't act as normal functions
▪ They generate next member when invoked
20. def _iter(size=100):
return iter(python_better_approach(size=size))
def lazy_python_approach(size=100):
for item in range(10, size):
if not item % 2 and not str(item).endswith("4"):
yield item
def lazy_new_python_approach(size=100):
yield from (r for r in range(10, size) if not r % 2 and not str(r).endswith("4"))
21. Recursion vs loop
• Functional programming requires a recursion approach vs loop iteration
• Python suffers by recursion limit
• Python does not offer any tail call optimization
22. def fact_loop(n):
if n == 0: return 1
f= 1
for i in range(2,n):
f *= i
return f
def fact(n):
if n == 0: return 1
else: return n*fact(n-1)
31. Monads
• How to handle errors in functional programming?
• Practical example: parsing and transforming data coming from HTTP request
• Python "std" libs have no support
35. Useful libraries
• Fn.py (https://github.com/kachayev/fn.py) (A bit of Scala, ported to Python)
• Underscore.py (ported from underscore_js)
36. Summary
• Functional programming for writing more succint code
• Change viewpoint: think in terms of functions
• Python seems for dummies until you show some cool features
• Python can be slow but with a fine tuning can be more efficient