Slide 1

Slide 1 text

Functional Programming in Python Shuhsi Lin 20181017 at PyHUG

Slide 2

Slide 2 text

About me 2 Data Engineer in a manufacturing company Working with • data and people Focus on • Streaming process • IoT applications • Data visualization • Engineer culture Shuhsi Lin Lurking in PyHug, Taipei.py and various Meetup sucitw gmail.com

Slide 3

Slide 3 text

Agenda 1. Basic Concept of Functional Programming ∙ Procedural vs Functional 2. Lambda Expression/ Nameless Function 3. First-Class/High-Order Functions 4. Common Patterns ∙ Partial Function Application ∙ Currying 5. More functional thinking 6. More functional programming in Python 3

Slide 4

Slide 4 text

4 What we will focused 1. What is Functional Programming 2. Some common patterns 3. Writing code with a better functional style 4. A path to dive into functional programming in python

Slide 5

Slide 5 text

“ 5 What will be not focused 1. Compare with other programming languages 2. OO in Python 3. Combine and switch back and forth with OO and procedural style

Slide 6

Slide 6 text

Computer Programming as an Art

Slide 7

Slide 7 text

Have you heard Programming Paradigm?

Slide 8

Slide 8 text

Common Programming Paradigm ● Imperative ○ Tell the “machine” how to do something, and as a result what you want to happen will happen (changes in state.) ○ Use statements that change a program state ■ Procedural (Languages with functions, C, Pascal, and even Unix shells) ■ Object-oriented (Java, smalltalk) ● Declarative ○ Tell the “machine”1 what you would like to happen, and let the computer figure out how to do it. ■ Functional: Evaluation of mathematical functions and avoids state and mutable data (Haskell, Scheme, OCaml) ■ Logic (Prolog, Clojure core.logic) ■ Mathematical 8 https://en.wikipedia.org/wiki/Programming_paradigm

Slide 9

Slide 9 text

9 Photo by Mitch Lensink on Unsplash Programming Paradigms are similar to painting or music styles Python are multi-paradigm

Slide 10

Slide 10 text

So, what is Functional Programming

Slide 11

Slide 11 text

Functional Programming ● Has a long story ● Lisp 1958 ● Renaissance: F#, Haskell, Erlang ● Functions as the fundamental building blocks for any program ● Focus on computing results rather than on performing actions ● Describe what is to be done and not on how it is to be done ● Leads to concise and safe code (less OffByOne mistakes) 11

Slide 12

Slide 12 text

Features of Functional Programming ● 不變性 ● 惰性求值 ● 高階函數 ● 無副作用 ● 一切皆函數 12 ● Immutability ○ Immutable data structures ○ Preserve state in functions (Avoid state) ● Lazy evaluation ● First-class/Higher-order functions ● Pure functions without side effects ■ Every function’s output must only depend on its input ■ Don’t change anything outside of the function ● Everything is a function ● Recursion instead of loops/iteration (Avoiding Flow Control)

Slide 13

Slide 13 text

13 Photo by Valerie Elash on Unsplash Why Should I care

Slide 14

Slide 14 text

Hmm… Why Should I care ● Better understanding other’s codes ● Big Data and Distributed Computing ○ Race condition (Multiple threads, Concurrency) ○ MapReduce ○ Spark ● Deep Learning ○ "weight tying" - using multiple copies of a neuron in different places is the neural network equivalent of using functions

Slide 15

Slide 15 text

Advantages of Functional Programming ● More modular and in smaller building blocks ● Better testable (same parameters return same result) ● Focus on algorithms ● Robust (Absence of side effects) ○ Referential Transparent (Stateless and no side effect) ○ Conceptional fit with parallel/ concurrent programming ○ Can be tested separately (isolation) ● Live updates-Install new release while running 15 ● Formal provability ● Modularity ● Composability ● Ease of debugging and testing https://xkcd.com/1312/

Slide 16

Slide 16 text

Python’s Functional Features ● Pure functions (sort of) ● Recursion- try to avoid, recursion limit has a reason ● Closures- hold state in functions ● Functions as object and decorators ● Immutable data types ● Lazy evaluation -generators ● List (dictionary, set) comprehensions ● Functools, itertools, lambda, map, filter 16

Slide 17

Slide 17 text

Pro and Con for FP in Python 17 Pro ● Functions as first-class citizens ● lambda ● Standard library: map/filter/reduce, itertools, operator ● Generators can be used for lazy-evaluation (in some cases) Con ● Impossible to separate pure / non-pure ● Mutable variables ● Costly mem copy operations ● Imperative style for cycles ● No optimization for tail recursion ● No pattern matching syntax ● Classes-based only type system ● No functions overloading mechanism ● Functions composition is not implemented in stdlib ● Imperative errors handling based on exceptions Python does not promote functional programming even though it works fairly well.

Slide 18

Slide 18 text

Message from our BDFL 18 http://python-history.blogspot.in/2009/04/origins-of-pythons-functional-features.html Origins of Python's "Functional" Features I have never considered Python to be heavily influenced by functional languages, no matter what people say or think. I was much more familiar with imperative languages such as C and Algol 68 and although I had made functions first-class objects, I didn't view Python as a functional programming language. However, earlier on, it was clear that users wanted to do much more with lists and functions. …. It is also worth nothing that even though I didn't envision Python as a functional language, the introduction of closures has been useful in the development of many other advanced programming features. For example, certain aspects of new-style classes, decorators, and other modern features rely upon this capability. Lastly, even though a number of functional programming features have been introduced over the years, Python still lacks certain features found in “real” functional programming languages. …. Guido van Rossum

Slide 19

Slide 19 text

19 Procedural VS. Functional

Slide 20

Slide 20 text

● Line-by Line ● Heavy use of Statements ● Also heavy use of Expressions ● Long functions 20 Procedural Functional ● Little use of Statements ● Heavy use of Expressions ● Single-line functions

Slide 21

Slide 21 text

21 $ ./program1 $ ./program2 --param1=1 $ ./program3 "Procedural terminal" "Functional terminal" $ ./program1 | ./program2 --param1=1 | ./program3 Procedural and Functional Style only one expression

Slide 22

Slide 22 text

22 Procedural and Functional Style A procedural approach! ● Functions generally consist of multiple statements ○ Assignments ○ If-statements ○ While loops ○ Etc. Imperative style = actions that change state from initial state to result expr, res = "28+32+++32++39", 0 for t in expr.split("+"): if t != "": res += int(t) print(res)

Slide 23

Slide 23 text

A functional approach! ● Functions consist of only one expression ● How can we validate input? (One of the many things we will learn later!) 23 # Functional style from operator import add expr = "28+32+++32++39" print(reduce(add, map(int, filter(bool, expr.split("+"))))) Functional style = apply transformation (and compositions) Procedural and Functional Style

Slide 24

Slide 24 text

Why should you avoid states and side effects? 24 Photo by Karsten Madsen on Unsplash

Slide 25

Slide 25 text

What is a State ● State (State and statelessness) ● Side effects ○ Global variables ○ Objects with properties ○ Will influence how functions work ● Referential transparency (引用透明) ○ An expression, in a program, may be replaced by its value (or anything having the same value) without changing the result of the program 25 https://stackoverflow.com/questions/210835/what-is-referential-transparency light.ison = true light.ison = false State change }

Slide 26

Slide 26 text

Stateless Functions ● Always return the same result if given the same arguments ● Regardless of the program's state 26 You know what you get Functions without side effects Leave the state alone ● Do not change the program’s state ● Related to but distinct from statelessness Demo

Slide 27

Slide 27 text

Purity ● Functions without “side effects” ● No I/O, global state change or DB interactions ● Limited number of arguments (preferably zero, any greater and may should reactor) ● Same input always yields same output Lead to: ● Few bugs ● More testable code ● Code reuse 27 # pure def add(a, b): return a + b additions_made = 0 # not pure def add(a, b): global additions_made additions_made += 1 return a + b

Slide 28

Slide 28 text

Recursion ● Functions often call themselves in FP ○ And again ○ And again ○ And again ○ …. ● Recursion is elegant (simpler or easier to read ) ● Difficult to follow ● Possible to be affected by language’s limitation (maximum recursion depth) ○ RecursionError: maximum recursion depth exceeded after 1000 recursive calls. ● memory intensive? 28 Demo

Slide 29

Slide 29 text

https://knowyourmeme.com/photos/1246322- sweet-jesus-pooh-thats-not-honey29 Tail Recursion ● A recursive function is tail recursive when a recursive call is the final execution by the function ● Avoid stack exhaustion ● Python don’t optimize tail calls ○ enter Trampolining

Slide 30

Slide 30 text

Tail Recursion 30 #not really tail recursive! def factorial(n): if n==0: return 1 else: return n*factorial(n-1) #tail recursive version def factorial(n, acum=1): if n==0: return acum else: return factorial(n-1, acum*n)

Slide 31

Slide 31 text

Lambda expressions 31

Slide 32

Slide 32 text

Statements and Expressions 32 Statement ● Procedural way ● Instructions (to write an interpreter) ● If, for ,def, class, and so on ● Don’t evaluate to something ● Cannot print the result Expressions ● Functional way ● Evaluate to something ● Can print the result ● Function calls are expressions Demo

Slide 33

Slide 33 text

Conditional Branching (Flow Control) ● Do A if X else do B ● “If” statement ○ Cannot be used in lambda expressions ● “If” expression ○ Can used in lambda expressions 33

Slide 34

Slide 34 text

34 Photo by Diomari Madulara on Unsplash First-Class/High-Order Function

Slide 35

Slide 35 text

35 Photo by Ben White on Unsplash Passing a Function as an Argument to another Function

Slide 36

Slide 36 text

36 Photo by JOSHUA COLEMAN on Unsplash Objects everywhere

Slide 37

Slide 37 text

First Class functions can: ● Function as object ● Be stored in variables ● Be returned from a function. ● Be passed as arguments into another function ● Support inline function definitions with anonymous lambda functions High Order Function ● A function that returns another function 37 First-Class/High-Order Function First-Class High-Order def add(a, b): return a + b add_function = add add = lambda a,b: a + b

Slide 38

Slide 38 text

High Order Function 38 ● A function that returns another function def timer(fn): def timed(*args, **kwargs): t = time() fn(*args, *kwargs) print "took {time}".format(time=time()-t) return timed def compute(): #... timed_compute = timer(compute) timed_compute() Return a function Function as an argument

Slide 39

Slide 39 text

Higher-Order Function ● A function can operate on other function 39 ● Everything is an object ● Passed to a function as an argument

Slide 40

Slide 40 text

40 Photo by Karen Ciocca on Unsplash Nested functions

Slide 41

Slide 41 text

41 Nesting a Function in Another Function ● Nested functions (Functions inside functions) ● Variable scope (Inside and outside of function) def outer(): def inner(): print('Inner:\t\t', x) print('Outer (before):\t', x) inner() print('Outer (after):\t', x) x = 'global' print('Global (before):', x) outer() print('Global (after): ', x) outside of function Global (before): global Outer (before): global Inner: global Outer (after): global Global (after): global def outer(): def inner(): nonlocal x x = 'inner' print('Inner:\t\t', x) x = 'outer' print('Outer (before):\t', x) inner() print('Outer (after):\t', x) x = 'global' print('Global (before):', x) outer() print('Global (after): ', x) Global (before): global Outer (before): outer Inner: inner Outer (after): inner Global (after): global Nonlocal binds a variable to one level higher

Slide 42

Slide 42 text

42 Photo by Caleb Lucas on Unsplash Functions return functions Demo

Slide 43

Slide 43 text

43 Decorators Dressing up a function https://en.wikipedia.org/wiki/Dog%27s_fashion

Slide 44

Slide 44 text

44 Decorators @timing def compute_magic(): dosomething Allow to implement higher order functions @a def b(): dosomething return somevalues

Slide 45

Slide 45 text

Lambda expression in Python 45 A Python lambda is just another method to define a function. General syntax lambda arguments: expression input do somethings

Slide 46

Slide 46 text

Dive into Lambda Expressions Lambda calculus ● Mathematical logic ● A formal mathematical system to express functions Lambda expression in Python ● Nameless /Anonymous functions ● Expressions, not statements ● Without a name 46 from math import sqrt def p_pythagoras(x, y): return sqrt(x**2 + y**2) p_pythagoras(1, 1) l_pythagoras = lambda x, y: sqrt(x**2 + y**2) lambda Useful when ● convenient if you quickly need a short function

Slide 47

Slide 47 text

Function Composition Combining functions 47 def compose(*functions): return functools.reduce(lambda f, g: lambda x: f(g(x)), functions, lambda x: x) import functools def compose(*functions): def compose2(f, g): return lambda x: f(g(x)) return functools.reduce(compose2, functions, lambda x: x) https://mathieularose.com/function-composition-in-python/

Slide 48

Slide 48 text

Reduce 48 functools.reduce(function, iterable[, initializer]) def reduce(function, iterable, initializer=None): it = iter(iterable) if initializer is None: value = next(it) else: value = initializer for element in it: value = function(value, element) return value Roughly equivalent to:

Slide 49

Slide 49 text

So now reduce(). This is actually the one I've always hated most, because, apart from a few examples involving + or *, almost every time I see a reduce() call with a non-trivial function argument, I need to grab pen and paper to diagram what's actually being fed into that function before I understand what the reduce() is supposed to do. So in my mind, the applicability of reduce() is pretty much limited to associative operators, and in all other cases it's better to write out the accumulation loop explicitly. Guido van Rossum, 2005 49 from functools import reduce >>> from functools import reduce >>> arr = [1, 2, 3, 4, 5] >>> reduce(lambda x, y : x+y, arr) 15 Voltron: teaching kids distributed computing since 1984 The fate of reduce() in Python 3000

Slide 50

Slide 50 text

50 Photo by rawpixel on Unsplash Operators Syntax or functions? from operator import *

Slide 51

Slide 51 text

Functional Design Patterns 51 Standard solution for common problems

Slide 52

Slide 52 text

Partial Function Application 52 Photo by Martin Brechtl on Unsplash

Slide 53

Slide 53 text

Partial Function Application 53 def add1(num): return add(1, num) add1(1) # simpler from functools import partial add1 = partial(add, 1) add1(1) def partial(func, *args, **keywords): def newfunc(*fargs, **fkeywords): newkeywords = keywords.copy() newkeywords.update(fkeywords) return func(*args, *fargs, **newkeywords) newfunc.func = func newfunc.args = args newfunc.keywords = keywords return newfunc What/How functools.partial does Example Return a new function Pre-assigned parameters Useful when ● Requires less argument The process of fixing a number of arguments to a function, producing another function of smaller arity

Slide 54

Slide 54 text

54 Photo by Thabang Mokoena on Unsplash Currying One argument per function Demo Takes multiple arguments into evaluating a sequence of functions, each with a single argument.

Slide 55

Slide 55 text

55 Currying Useful when ● Requires single (exactly one) argument Decomposition of a polyadic function into a chain of nested unary functions def outer(outer_arg): def inner(inner_arg): return inner_arg + outer_arg return inner func = outer(10) func(5) >>> 15

Slide 56

Slide 56 text

56 Currying from inspect import signature def curry(fnc): def inner(arg): if len(signature(fnc).parameters) == 1: return fnc(arg) return curry(partial(fnc, arg)) return inner @curry def add(a, b, c): return a + b + c add_10 = add(10) add_10_100 = add_10(100) print(add_10_100(1000)) from functools import partial add_10 = partial(add, 10) add_10_100 = partial(add_10, 100) print(add_10_100(1000)) def add(a, b, c): return a + b + c print(add(10,100,1000)) partial (Binding function arguments) currying by a decorator simple function with multiple arguments

Slide 57

Slide 57 text

57 https://hackernoon.com/kotlin-functors-applicatives-and-monads-in-pictures-part-1-3-c47a1b1ce251 A monad is a structure that puts a value in a computational context https://nikgrozev.com/2013/12/10/monads-in-15-minutes/ Imperative Composition Monad

Slide 58

Slide 58 text

print( bind(bind(bind(unit(x), f1), f2), f3) ) def f1(x): return (x + 1, str(x) + "+1") def f2(x): return (x + 2, str(x) + "+2") def f3(x): return (x + 3, str(x) + "+3") chain the functions log = "Ops:" res, log1 = f1(x) log += log1 + ";" res, log2 = f2(res) log += log2 + ";" res, log3 = f3(res) log += log3 + ";" print(res, log) “glue code” f3(f2(f1(x))) def unit(x): return (x, "Ops:") def bind(t, f): res = f(t[0]) return (res[0], t[1] + res[1] + ";") single chained function invocation Imperative

Slide 59

Slide 59 text

59 Photo by Ksenia Makagonova on Unsplash Disadvantages Oh~No!

Slide 60

Slide 60 text

Disadvantages of Functional Programming ● Solutions for the same problem are different than procedural/ Object-oriented ones ● Not equally useful for all type of problems ● Input/ output are side effects and need special treatment ● Recursion is “an order of magnitude more complex” than loops/iteration ● Immutable data structures may increase run times 60

Slide 61

Slide 61 text

Pro and Con for FP in Python 61 Pro ● Functions as first-class citizens ● lambda ● Standard library support: map/filter/reduce, itertools, operator ● Generators can be used for lazy-evaluation (in some cases) Con ● Difficult to separate pure / non-pure ● Mutable variables ● Costly mem copy operations ● Imperative style for cycles ● No optimization for tail recursion ● No pattern matching syntax ● Classes-based only type system ● No functions overloading mechanism ● Functions composition is not implemented in stdlib ● Imperative errors handling based on exceptions Python does not promote functional programming even though it works fairly well.

Slide 62

Slide 62 text

The Missing Pieces for Python ● Immutable Data Structures ● Pattern Matching ● Lazy Evaluation ● Tail Call Optimization (TCO) ● Large Collection of List functions 62

Slide 63

Slide 63 text

63 Recap ● What is Functional Programming Style ● Functional V.S. Procedural ● Common Functional Design Patterns ● Python offers functional features, but not pure functional language

Slide 64

Slide 64 text

Adopt Functional Thinking ● Break the process into discrete steps. ● 100 % pure FP may be difficult to be implemented ○ Avoid (mutable) state/side effect in a function, ■ __init__ ■ Staticmethod ■ Type checking/ annotation ○ Accept fewer side effects. Embrace Python’s OOP and functional styles. ○ Freeze classes (immutable and frozen data type) ● Pure functions as moules (Referential transparency) ● Create modified copies, rather than modifying existing objects ( ○ but expensive, so try to use persistent collections 64

Slide 65

Slide 65 text

Adopt Functional Thinking ● Develop a thinking pattern for why FP in your current code ○ Why currying, monadic, lambda .... ● Global variable is evil ● Small function is easy to be understood ● Rely on the standard lib. ○ operator, Itertools and functools 65 >>> ss = ["Hsinchu", "PyHug", "2018"] >>> reduce(lambda acc, s: acc + len(s), ss, 0) 16 >>> s s= ["Hsinchu", "PyHug", "2018"] >>> reduce(lambda l,r: l+r, map(lambda s: len(s), ss)) 16 >>> ss = ["Hsinchu", "PyHug", "2018"] >>> reduce(operator.add, map(len, ss)) 16

Slide 66

Slide 66 text

More about FP in Python ● Deep into closure ● Lazy evaluation (generator List comprehensions ● More itertools and functools ● Errors handling without exceptions ● More design patterns ○ Memoization, Tail Recursion, Mutual Recursion, Filter-Map-Reduce ● Functional data structures, Custom data types 66

Slide 67

Slide 67 text

Reference 67 1. Functional Programming HOWTO (Python Official Site) 2. Udemy (LEARNING PATH: Python: Functional Programming with Python) 3. https://github.com/xgrommx/awesome-functional-programming 4. https://github.com/sfermigier/awesome-functional-python 5. https://www.slideshare.net/ihower/fp-osdc2012v2 6. https://slides.com/apuaa-aa/python-fp#/ 2015 PyconAPAC by Apua 7. https://en.wikipedia.org/wiki/Lambda_calculus 8. https://nikgrozev.com/2013/12/10/monads-in-15-minutes/ more about Monad ● fn.py ★2600 - "Functional programming in Python: implementation of missing features to enjoy FP" (unmaintained since 2014). ● Coconut - "Simple, elegant, Pythonic functional programming".