# Introduction to Functional Programming

## Content

1. Recursion
2. High order function
3. Closure & lexical scope
4. Partial application & currying
5. Delay evaluation

## Intro: Put an elephant into a fridge

``````print 'open the fridge'
print 'put elephant into the fridge'
print 'close the fridge' ``````

## Put an elephant into a fridge

``````class Fridge:
def open(self):
print 'open the fridge'

def put(self, something):
print 'put %s into the fridge' % something

def close(self):
print 'close the fridge'

fridge = Fridge()
fridge.open()
fridge.put('elephant')
fridge.close() ``````

## Put an elephant into a fridge

``````def open(something):
print 'open the %s' % something
return something

def put(object, container):
print 'put %s into the %s' % (object, container)
return container

def close(something):
print 'close the %s' % something
return something ````close(put('elephant', open('fridge')))````

# 1. Recursion

In order to understand recursion,
you must first understand recursion.

## Why recursion?

Wikipedia:
In computer science, functional programming is a programming paradigm, a style of building the structure and elements of computer programs, that:
• Treats computation as the evaluation of mathematical functions
• Avoid state and mutable data

## Treats computation as the evaluation of mathematical functions

Functions: take in values and return values

Statements do something:
``````>>>i = i + 1
>>> ``````
Use statements?  No!

Expression produce value:
``````>>>i + 1
43
>>> ``````
Use expression?  Yes!

## Avoid state and mutable data

Loops:
``````def fac(n):
n = 1 #mutation
for i in range(n): #states
n = n * (i + 1) #mutation
return n ``````

Recursion:
``````def fac(n):
if n == 0:
return 1
else:
return n * fac(n - 1) ``````

## So how to write recursion?

How to compute exponential of a given number?
``b^n = b * b * b ... * b (b occurs n times)``
So:
``````def exp(b, n):
result = 1
for _ in range(n):
result *= b
return result``````

## So how to write recursion?

How to compute exponential of a given number?
``b^n = b * b^(n - 1)``b^0 = 1``

So:
``````def exp(b, n):
if n:
return b * exp(b, n - 1)
else:
return 1``````

## So how to write recursion?

How to compute exponential more efficiently?

``````b^n = b^(n/2) * b^(n/2)  (if n is even)
b^n = b * b^(n-1) (if n is odd)
b^0 = 1 ``````

So

``````def exp(b, n):
if n%2:
return b * exp(b, n - 1)
elif n:
return exp(b, n/2) * exp(b, n/2)
else:
return 1 ``````

## Another example

Fibonacci sequence

``````def fib(n):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2) ``````

However, this solution has a big problem

## If we trace call chain of recursive fib(n)

``````fib(4) called [#1]
--fib(3) called [#2]
----fib(2) called [#3]
------fib(1) called [#4]
------fib(1) returned 1 [#4]
------fib(0) called [#5]
------fib(0) returned 0 [#5]
----fib(2) returned 1 [#3]
----fib(1) called [#6]
----fib(1) returned 1 [#6]
--fib(3) returned 2 [#2]
--fib(2) called [#7]
----fib(1) called [#8]
----fib(1) returned 1 [#8]
----fib(0) called [#9]
----fib(0) returned 0 [#9]
--fib(2) returned 1 [#7]
fib(4) returned 3 [#1] ``````
fib(4) called fib() 9 times
fib(5) called fib() 15 times
fib(6) called fib() 25 times
...

## Tail Recursion

Convert recursive process to iterative process.
``````def fib(n):
def fib_iter(a, b, n):
if n:
return fib_iter(b, a + b, n - 1)
else:
return a
return fib_iter(0, 1, n) ``````

• track state by function arguments
• reduce length of call stack and improve efficiency
(not supported in python yet)

# function

## First class function

First class function means functions are treated as first class element by programming language. Like objects in OOP.

Privileges of first class elements includes
• They may be named by variables.
• They may be passed as arguments to functions.
• They may be returned as the results of procedures.
• They may be included in data structures.

## Functions can be named by variables

• Anonymous function:
``````>>> (lambda x: x * x)(2)
4``````
• Function s can be assigned to variables:
``````>>> square = lambda x: x * x
>>> square(2)
4``````
• Or make alias easily:
``````>>> sq = square
>>> sq(2)
4 ``````

## Function passed as arguments

• The well known build in functions
• map(), filter(), reduce()

• sorted()
``````>>> name = ['Leonardo DiCaprio', 'Johnny Depp', 'Tom Cruise']
>>> sorted(name) #sort by full name
['Johnny Depp', 'Leonardo DiCaprio', 'Tom Cruise']
>>> sorted(name, key = lambda x: x.split(' ')) #sort by last name
['Tom Cruise', 'Johnny Depp', 'Leonardo DiCaprio']
>>> sorted(name, key = lambda x: len(x)) #sort by name length
['Tom Cruise', 'Johnny Depp', 'Leonardo DiCaprio'] ``````
• get_cursors_if
``get_cursors_if(source, satisfy_func, transform_func) ``

## Function as return value

• A tiny example:
``````>>> def addn(n):
return m + n

5 ``````

• Memoization

## Memoization

``````def fib(n):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2) ``````

Recall the recursive version of fib(),it is in very low efficient because of redundant computation. What if it can store the computed result?

## Memoization

``````def memoize(f):
cache = {}
def g(x):
if x not in cache:
cache[x] = f(x)
return cache[x]
return g

fib = memoize(fib) ``````>>> fib(5)
fib(5) called [#1]
fib(4) called [#2]
fib(3) called [#3]
fib(2) called [#4]
fib(1) called [#5]
fib(1) returned 1 [#5]
fib(0) called [#6]
fib(0) returned 0 [#6]
fib(2) returned 1 [#4]
fib(3) returned 2 [#3]
fib(4) returned 3 [#2]
fib(5) returned 5 [#1]``````

## Trace

High order function that can trace function call.
``````__report_indent = 

def trace(fn):
def wrap(*params,**kwargs):
call = wrap.callcount = wrap.callcount + 1

indent = ' ' * __report_indent
fc = "%s(%s)" % (fn.__name__, ', '.join(
[a.__repr__() for a in params] +
["%s = %s" % (a, repr(b)) for a,b in kwargs.items()]
))

print "%s%s called [#%s]"\
% (indent, fc, call)
__report_indent += 1
ret = fn(*params,**kwargs)
__report_indent -= 1
print "%s%s returned %s [#%s]"\
% (indent, fc, repr(ret), call)

return ret
wrap.callcount = 0
return wrap ``````

## Decorator

Special syntax in Python:
``fib = memorize(fib) ``
can be written as:
``````@memorize
def fib(n):
... ``````

# and lexical scope

## Closure

• A closure, like an object instance, is a way of carrying around a bundle of data and functionality, wrapped up together.

• Lexical scope is a nature way to implement closure.

## Closure

``````def addx(x):
def func(y):
return x + y
return func
x = 3
>>>foo(10)
15 # in lexical scope, 5 is bind to x in foo``````

In lexical scope, the body of a function is evaluated in the environment where the function is defined, not the environment where the function is called. By working in this way, it binds data with functions.

## Alternative to closure

````#Argument passing````def addx(x):
def func(a, b = x):
return a + b
return func
>>> a(3)
5 ``````
````#use object````class addx():
def __init__(self, x):
self.x = x

def __call__(self, y):
return self.x + y
>>> a(3)
5 ``````
Anything done with closure can be done without closure.
But closure provides a more clear and simple solution.

## Alternative to closure

The venerable master was walking with his student.  The student said "Master, I have heard that objects are a very good thing - is this true?"  Master looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures."
On his next walk with master, student said "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures."  Master responded by hitting student with his stick, saying "When will you learn? Closures are a poor man's object."  At that moment, the student became enlightened.
http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html

## Special notice for python

Reassign closure variable of immutable type inside function will lead to unexpected result.
``````def counter():
count = 0
def func():
count += 1
return count
return func
>>> count = counter()
>>> count()
Traceback (most recent call last):
File "", line 1, in
count()
File "C:/Users/JC/Desktop/a.py", line 152, in func
count += 1
UnboundLocalError: local variable 'count' referenced before assignment
``````

## Special notice for python

A common solution is to replace immutable objectwith mutable object

``````def counter():
count = 
def func():
count += 1
return count
return func
>>> count = counter()
>>> count()
1
>>> count()
2 ``````

# Currying

## Partial application

Partial function application is about fixing some arguments of a given function to yield another function with fewer arguments.

get_cursors_if
``get_cursors_if(source, satisfy_func, transform_func)``get_cursor_names_if = partial(get_cursors_if,``    transform_func = lambda c: c.displayname)``get_class_cursor = partial(get_cursors_if,``    satisfy_func = lambda c: c.kind == CursorKind.CXX_CLASS,``    transform_func = lambda c: c)``

## Partial application

Consider the following Ackermann function which computes hyperoperations of 2.
``````def Ackermann(x, y):
if not y:
return 0
elif not x:
return 2 * y
elif y == 1:
return 2
else:
return A(x - 1, A(x, y - 1)) ``````

## Hyperoperations ## Partial application

Ackermann(x, y) computes y times hyper(x - 2) of base 2
``````>>> Ackermann(0, 4)
8                      #hyper 2  multipication 2 * 4
>>> Ackermann(1, 4)
16                     #hyper 3 exponentiation 2 ^ 4``````>>> Ackermann(2, 3)
16                     #hyper 4 tetration  2 ^^ 3
>>> Ackermann(2, 4)
65536                  # 2 ^^ 4``````

## Partial application

• With partial application, we can generate functions to compute hyper n
``````def partial_Ack(n):
def func(b):
return Ackermann(n, b)
return func``````multiply2 = partial_Ack(0)
exp2 = partial_Ack(1)
teration2 = partial_Ack(2) ``````
• partial() function provided by functools provides a move convenient way for partial application
``````from functools import partial

multiply = partial(Ackermann, 0) ``````

## Partial application

Why partial application?
• Convenience
• Bind data with function to build more specific function
• Encapsulation and detail hidding

## Currying

Transforming a function that takes multiple arguments (or a tuple of arguments) in such a way that it can be called as a chain of functions

``````def func(a, b, c):
return a + b + c

def curried_func(a):
return lambda b: lambda c: a + b + c

>>> print func('a', 'b', 'c')
abc
>>> print curried_func('a')('b')('c')
abc ``````

## Currying

• Currying is close related to partial application
• Currying can transfer multiple argument function to a chain of single argument function which is very useful in old days when functions can only take in one argument.
• Nowadays, currying is generally considered as language support for partial application. It can be replaced by partial application, thus it is seldom used in languages that doesn't support currying.
````computation a b c d = (a + b^2+ c^3 + d^4)````
fillOne   = computation 1
fillTwo   = fillOne 2
fillThree = fillTwo 3
-- Result: answer == 657 ``````

# 5. Delay evaluation

## Implementing my_if()

Python do not support ternary operator like ( ? : ) in C++, what if we want to write if statement in one line?

one solution: use "and" and "or"

``````#a ? b : c
(a and b) or c ``````

an other solution: implement an my_if function

``````#a ? b : c
my_if(a, b, c) ``````

## Implementing my_if()

def my_if(a, b, c):
if a:
return b
else:
return c

This solution looks just like syntax sugar of if statement.
However, it does not work.

## Implementing my_if()

Consider the following situation:

``````def bad_exp():
while True:
pass ``````

The If statement works well:

``````if True:
good_exp()
else:

But my_if() will run into infinity loop:

``my_if(True, good_exp(), bad_exp()) ``

Because function arguments will be evaluated first.

## Delay evaluation

In order to delay evaluation of statements, we can wrap statements into functions. Functions are evaluated to themselves and let function call evaluated to statements.

Modified my_if():
``````def my_if(a, b, c):
if a:
return b()
else:
return c() ``````

To call my_if():
``my_if(a, lambda : b, lambda : c) ``

## Delay evaluation

Delay the evaluation of parameter by putting it into a wrapper function. Then call the function to get the value of parameter. The wrapping process is called ‘thunk’.

However, if use delay evaluation, parameter will be computed every time when it is used.

## Stream

If we need a sequence but we don't know exactly how long is needed. We can delay the evaluation of sequence, and evaluate it one by one when needed. This kind of object is called stream

``````def fib():
def stream(a, b):
return (a, lambda : stream(a + b, a))
return stream(1, 0) ``````

above if a stream than generates Fibonacci numbers. Every time when fib() is called, it generate a tuple of fib number and delayed fib() for next element.

## Stream

To get first element of stream:
``````>>> fib()
1 ``````
To get the second one:
``````>>> fib()()
1 ``````
To get the nth:
``````def get_nth(stream, n):
val, next_str = stream()
if n != 1:
return get_nth(next_str, n - 1)
else:
return val ``````>>> get_nth(fib, 10)
55``````

## Stream example: twin prime

Twin prime means two prime that one prime is different with the other by two, for example (3, 5), (17, 19). There is said to have infinity number of twin primes.

``````def isprime(n):
for x in xrange(2, int(n**0.5)+1):
if n % x == 0:
return False
return True

def prime():
def stream(n):
if isprime(n):
return (n, lambda : stream(n + 1))
else:
return stream(n + 1)
return stream(2)  ``````

## Stream example: twin prime

``````def twin_prime():
def stream(n):
if isprime(n) and isprime(n + 2):
return ((n, n + 2), lambda : stream(n + 1))
else:
return stream(n + 1)
return stream(2)

>>> for i in range(1, 5):
. . . 	print get_nth(twin_prime, i)

(3, 5)
(5, 7)
(11, 13)
(17, 19)``````

## Generator

Generator object provided by python is also very suitable for generating sequence of infinity length.

``````def gen_prime():
n = 2
def gen(n):
if isprime(n):
return n
else:
return gen(n + 1)
while True:
n = gen(n)
yield n
n += 1 ``````

## Generator

Generation version of twin_prime:
``````def gen_twin_prime():
prime = gen_prime()
a = prime.next()
def gen(a):
b = prime.next()
if b - a == 2:
return (a, b)
else:
return gen(b)
while True:
tmp = gen(a)
a = tmp
yield tmp ``````
Ideas of stream and generator are almost the same.

Generator has more language support.

## PF and OOP

Let's go back to the fridge example.

Suppose we want to put elephant into the zoo.

OOP:
``````class Zoo:
def open(self):
...
def put(self, object):
...
def close(self):
... ``````

## PF and OOP

Let's go back to the fridge example.

Suppose we want to put elephant into the zoo.

FP:
``````def open(something):
if something == 'fridge':
...
elif something == 'zoo':
...

def put
if... ````def close``    if... ````

## PF and OOP

Now, we want to add clean() method for fridge and zoo
OOP:
``````class Fridge:
...
def clean(self):
print 'clean the fridge'

class Zoo:
...
def clean(self):
print 'clean the zoo' ``````

## PF and OOP

Now, we want to add clean() method for fridge and zoo
FP:
``````def clean(something):
if something == 'fridge':
print 'clean the fridge'
elif something == 'zoo':
print 'clean the zoo'
return something ``````

## PF and OOP #### Intro to FP

By Jingchuan Chen

• 2,354