A brief introduction to functional programming.
Even if slides present some simple Python code, functional programming patterns applies to other languages too.
Unlocking the Future of AI Agents with Large Language Models
Introduction to Functional Programming
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