—
Data Scientist @ idalab (mainly Python) Used Ruby, JS, Python, Haskell, Swift for nontrivial projects Played with Clojure, Scala, Erlang, Elixir
—
- Not a motivation of functional programming
- How can FP by used in Python
—
There should be one — and preferably only one — obvious way to do it. -- PEP 20 — The Zen of Python
^ Python has a philosophy and many of the things you will see go against this philosopy This goes so far that
—
The fate of reduce() in Python 3000
not having the choice streamlines the thought process -- Guido van Rossum
^ 2005 Disagree: language should support me Don’t recommend you to use what I’m presenting here Show you the entrance to the rabbit hole Jupyter Notebook! Don’t be afraid to interrupt me!
—
- first class functions
- higher order functions
- purity
- immutability
- composition
- partial application & currying
- recursion
^ Vocabulary of fuctional concepts
—
- first class functions
- higher order functions
- purity
immutability(not today)- composition
- partial application & currying
recursion(neither)
—
functions without side-effects
defadd(a, b):
returna+badditions_made=0defadd(a, b):
globaladditions_madeadditions_made+=1returna+b—
defadd(a, b):
returna+badd_function=addadd=lambdaa,b: a+b—
deftimer(fn):
deftimed(*args, **kwargs):
t=time()
fn(*args, *kwargs)
print"took {time}".format(time=time()-t)
returntimeddefcompute():
#…timed_compute=timer(compute)
timed_compute()—
@timerdefcompute():
sleep(1)
compute()—
defadd1(num):
returnadd(1, num)
add1(1)
# simplerfromfunctoolsimportpartialadd1=partial(add, 1)
add1(1)^ Toy example - just building up a toolbox for the fun stuff
—
[…] transforming a function that takes multiple arguments in such a way that it can be called as a chain of functions, each with a single argument (partial application) — Wikipedia
—
defcurried_add(a):
definner(b):
returnadd(a,b)
returninneradd(1) # => <function …>add(1)(1) # => 2—
fromtoolzimportcurryadd=curry(add)
add(1) # => <function …>add(1, 1) # => 2—
defcurried_add(a):
definner(b):
returnadd(a,b)
returninneradd(1) # => <function …>add(1)(1) # => 2—
obj.method()
fromoperatorimportmethodcallermethodcaller("method")(obj)^ We will see how this is useful
—
—
map(f, iter)
[f(el) forelinseq]—
filter(p, seq)
[elforelinseqifp(el)]—
fromfunctoolsimportreducereduce(f, seq, initial)
result=initialforelinseq:
result=f(result, el)—
^ 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
—
[f(x) forxinseqifp(x)]
map(f, filter(p, seq))
fromtoolz.curriedimportcompose, map, filtercompute=compose(map(f), filter(p))
compute(seq)^ Illustrational purposes
—
csv="""firstName;lastNameJim;DrakeBen;JamesTim;Banes"""target= [{'firstName': 'Jim', 'lastName': 'Drake'},
{'firstName': 'Ben', 'lastName': 'James'},
{'firstName': 'Tim', 'lastName': 'Banes'}]—
lines=csv.split("\n")
matrix= [line.split(';') forlineinlines]
header=matrix.pop(0)
records= []
forrowinmatrix:
record= {}
forindex, keyinenumerate(header):
record[key] =row[index]
records.append(record)—
fromtoolz.curriedimportcompose, mapfromfunctoolsimportpartialfromoperatorimportmethodcallersplit=partial(methodcaller, 'split')
split_lines=split("\n")
split_fields=split(';')
dict_from_keys_vals=compose(dict, zip)
csv_to_matrix=compose(map(split_fields), split_lines)
matrix=csv_to_matrix(csv)
keys=next(matrix)
records=map(partial(dict_from_keys_vals, keys), matrix)—
docker run --rm -v ${PWD}:/home/jovyan/work -p 8888:8888 jupyter/pyspark-notebookdefsample(p):
x, y=random(), random()
return1ifx*x+y*y<1else0count=sc.parallelize(range(0, NUM_SAMPLES)) \
.map(sample) \
.reduce(lambdaa, b: a+b)
print("Pi is roughly %f"% (4.0*count/NUM_SAMPLES))^ http://spark.apache.org/docs/latest/programming-guide.html#transformations
—
defkmeans(points, k):
returnuntil_convergence(
iterate(
find_new_means(points),
random.sample(points, k)))—
defuntil_convergence(it):
returnlast(accumulate(no_repeat, it))
defno_repeat(prev, curr):
ifprev==curr: raiseStopIterationelse: returncurr—
importrandomfromtoolz.curriedimportiterate, accumulate, curry, groupby, last, composedefkmeans(k, points):
returnuntil_convergence(iterate(find_new_means(points), random.sample(points, k)))
@currydeffind_new_means(points, old_means):
k=len(old_means)
clusters=groupby(compose(str, closest_mean(old_means)), points).values()
returnlist(map(cluster_mean, clusters))—
@currydefclosest_mean(means, point):
returnmin(means, key=squared_distance(point))
@currydefsquared_distance(p, q):
returnsum((p_i-q_i)**2forp_i, q_iinzip(p, q))—
defcluster_mean(points):
num_points=len(points)
dim=len(points[0]) ifpointselse0sum_points= [sum(point[j] forpointinpoints)
forjinrange(dim)]
return [s/num_pointsforsinsum_points]—
- FP is possible in Python (to a degree)
- small composable functions are good
- FP == build general tools and compose them
^ Functional programming enables writing small composable functions Decide for yourself and with your team if this is a good idea
—
- More list functions
- Nicer lambda syntax
- Automatic currying, composition syntax
- ADTs (sum types)1
- Pattern Matching
—
- http://toolz.readthedocs.io/en/latest/
- https://github.com/kachayev/fn.py
- http://pedrorodriguez.io/PyFunctional/
^ Matthew Rocklin last year’s keynote ALEXEY KACHAYEV
—
map(lambdax: x**2, range(5)) # => [0, 1, 4, 9, 16]fromfnimport_map(_**2, range(5)) # => [0, 1, 4, 9, 16]^ Might not work as expected in i.e. PySpark
—
- Separation of pure code and sideeffects: https://pypi.python.org/pypi/effect/
- Persistent immutable data structures https://pypi.python.org/pypi/pyrsistent/
- https://docs.python.org/3/howto/functional.html
—
- http://kachayev.github.io/talks/uapycon2012/
- https://vimeo.com/80096814
- https://github.com/joelgrus/stupid-itertools-tricks-pydata
- http://kirelabs.org/fun-js
^ Matthew Rocklin PyData NYC 2013 - pytoolz ALEXEY KACHAYEV PyCon UA 2012 - fn.py Joel Grus: PyData Seattle 2015
—
- SICP (http://deptinfo.unice.fr/~roy/sicp.pdf)
- http://learnyouahaskell.com/
- Real World Haskell (http://book.realworldhaskell.org/read/)
—
Daniel Kirsch daniel.kirsch@idalab.de@kirelhttps://github.com/kirel/functional-python
Footnotes
Possible but ugly http://stupidpythonideas.blogspot.de/2014/08/adts-for-python.html↩

