Introduction to Functional Programming
-
Upload
francesco-bruni -
Category
Software
-
view
97 -
download
2
Transcript of Introduction to Functional Programming
![Page 1: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/1.jpg)
Introduction to functional programming in Python Francesco Bruni
@brunifrancesco
Original notebook @ https://github.com/brunifrancesco/cds_talk
![Page 2: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/2.jpg)
Who am I • MSc Student in Telecommunication Engineering @ Poliba (Italy)• Despite a Java background, I prefer Python whenever possible• Data science addicted
![Page 3: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/3.jpg)
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
![Page 4: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/4.jpg)
Why functional programming • Think in terms of functions
• State as function evaluation, not variables assignment
• Testing is (more) easy
• A new viewpoint is required
![Page 5: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/5.jpg)
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))
![Page 6: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/6.jpg)
Why PythonThere are a lot of languages out there..
![Page 7: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/7.jpg)
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
![Page 8: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/8.jpg)
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
![Page 9: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/9.jpg)
Functional features in Python • Not all functional patterns apply to Python
• Hybrid approach is often requested
• Other libraries needs to be (eventually) integrated
![Page 10: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/10.jpg)
First class functions • Functions are objects too
• They have fields too and some basic methods
![Page 11: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/11.jpg)
def func_1(x="32"):
assert(x)
def func_2():
return "wow!"
func_3 = lambda some_param: str(some_param)
![Page 12: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/12.jpg)
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.
![Page 13: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/13.jpg)
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))
![Page 14: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/14.jpg)
Class for function compositionHandle class creation via a new class syntax, allowing a "static" configuration
![Page 15: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/15.jpg)
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 ** 2ex_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())
![Page 16: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/16.jpg)
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
![Page 17: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/17.jpg)
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 ** 2ex_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())
![Page 18: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/18.jpg)
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
![Page 19: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/19.jpg)
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
![Page 20: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/20.jpg)
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"))
![Page 21: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/21.jpg)
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
![Page 22: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/22.jpg)
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)
![Page 23: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/23.jpg)
CurryingMultiple arguments functions mapped to single arguments functions
![Page 24: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/24.jpg)
from random import randrange
def mult(a, b, c ):
def wrapper_1(b):
def wrapper_2(c):
return a*b*c
return wrapper_2(c)
return wrapper_1(b)
def mult_2(a):
def wrapper_1(b):
def wrapper_2(c):
return a*b*c
return wrapper_2
return wrapper_1
![Page 25: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/25.jpg)
PartialsPython provides "partial" functions for manual currying
![Page 26: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/26.jpg)
from functools import reduce
import operator
import random
def two_random_sum():
return reduce(operator.sum, [random.random()]*2)
def three_random_product():
return reduce(operator.mul, [random.random()]*3)
def four_random_sub():
return reduce(operator.sub, [random.random()]*4)
def five_random_pow ():
return reduce(operator.pow, [random.random()]*5)
![Page 27: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/27.jpg)
def handle_random_numbers(size, function):
return reduce(function, [random.random()]*size)
two_random_sum = partial(handle_random_numbers, size=2)three_random_sum = partial(handle_random_numbers, size=3)
two_random_pow = partial(handle_random_numbers, size=2, function=operator.pow)five_random_product = partial(handle_random_numbers, size=5, function=operator.mul)
![Page 28: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/28.jpg)
Decorator PatternReturn a modified version of a decorated function
![Page 29: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/29.jpg)
from functools import wrapsfrom functools import partial
def get_ned_data(n):
def get_doubled_data(func, *args, **kwargs): @wraps(func)
def _inner(*args, **kwargs): kwargs["new_param"] = kwargs["some_param"]*n
return func(*args, **kwargs)
return _inner
return get_doubled_data
![Page 30: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/30.jpg)
@get_ned_data(n=2)def double_func(*args, **kwargs): assert(kwargs["new_param"] == kwargs["some_param"]*2)
@get_ned_data(n=3)def triple_func(*args, **kwargs): assert(kwargs["new_param"] == kwargs["some_param"]*3) double_func(some_param=3)triple_func(some_param=5)
![Page 31: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/31.jpg)
Monads • How to handle errors in functional programming?
• Practical example: parsing and transforming data coming from HTTP request
• Python "std" libs have no support
![Page 32: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/32.jpg)
from fn.monad import optionable
def get(request, *args, **kwargs):
@optionable
def _get_values(data):
return data.get("values", None)
_split = lambda item: item.split(",")
_strip = lambda item: item.replace(" ", "")
_filter = lambda item: list(filter(lambda i: i, item))
values = _get_values(request.GET).map(_strip).map(_split).map(_filter).get_or(["v1,v2"])
return values
![Page 33: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/33.jpg)
from collections import namedtuple
def test():
_req_class = namedtuple("Request", ("GET"))
request_1 = _req_class(dict(values="v1, v2,v3"))
request_2 = _req_class(dict(values="v1,v2,v3 "))
request_3 = _req_class(dict(values="v1, v2,v3, "))
assert(get(request_1) == ['v1', 'v2', 'v3'])
assert(get(request_2) == ['v1', 'v2', 'v3'])
assert(get(request_3) == ['v1', 'v2', 'v3'])
![Page 34: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/34.jpg)
@get_object(MeterInstance) def post(self, request, *args, **kwargs): @optionable def get_data(data): return data.get("data", None) return get_data(request.DATA)\ .map( lambda item: handle_error_configuration(item, kwargs["contatore"]))\ .map( lambda item: Response(dict(detail=item), status=200))\ .get_or(Response("BadRequest", status=400))
![Page 35: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/35.jpg)
Useful libraries• Fn.py (https://github.com/kachayev/fn.py) (A bit of Scala, ported to Python)
• Underscore.py (ported from underscore_js)
![Page 36: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/36.jpg)
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
![Page 37: Introduction to Functional Programming](https://reader033.fdocuments.us/reader033/viewer/2022042716/55cd87cabb61ebd3628b47ab/html5/thumbnails/37.jpg)
Questions?