Lightning Talk
paufler.net@gmail.com

The Functional Programming Paradigm

Not so much a How, as a Why.



My Stack

A forward looking statement, at best.


I know.
I don't know.
Has a Functional basis.


Bona Fides

Be impressed.
Be very impressed. /s


Haskell for 3 months in a single problem domain (Project Euler).

No CS, BS, MS, or Phd
Not a PL guy (Project Language)
No Category Theory
None of Church's Lambda Calculus

So, may be a case of the blind leading the blind.
Or the blissfully ignorant demonstrating their stupidity.
I'll let you decide.


Aspire to be stupid.
Dare to be wrong.


Functional Programming is Not a Look or a Feel

No matter how you may feel about that.


#Find Squares of a List
a = [1, 2, 3, 4]

def f(n):
    '''Return square of n.'''
    return pow(n, 2)

x = []
for v in a:
    x.append(f(v))

y = [f(b) for b in a]
	
z = map(f, a)

print x     #[1, 4, 9, 16]
print y     #[1, 4, 9, 16]
print z     #[1, 4, 9, 16]


f(n) is as functional as it gets.
As to x, y, & z, all three get the job done.
They are all Functionally equivalent.


def mess(x):
    '''Bad version of pow(x, 2).'''
    x = int(x)
    print 'mess running: %d' % x
    x = x * x
    print 'mess returning: %d' % x
    if x:
        return x
    else:
        return None
		
print map(mess, a)
#[1, 4, 9, 16]


But we can abstract it and not worry about any of that.


g = mess
print map(g, a)
#[1, 4, 9, 16]

for_loop = map
print for_loop(g, a)
#[1, 4, 9, 16]


The difference between f(x) or g(x) & map(f, x) or for_loop(g, a)is purely aesthetic.

Functional Programming is not an aesthetic cult (an ascetic cult maybe, but that's a talk for a different day).


A Bold Claim

Immutability
Is
The Heart of Functional Programming


#Functional           #Non-Functional
x = 1                 x = 1
y = x + 1             x = x + 1

z = map(f, x)         x = map(f, x)


In Functional Programming, re-assignment of variables is not allowed.
That's it.
That's the secret.
That's Functional Programming in a nutshell.


But It's All a Lie

Even in Functional Programming things are mutated all the time.

Perhaps even more damning, Haskell's Hello World is procedural.

--Sample Haskell 'Hello World' Code
main :: IO ()
main = do
    putStrLn "Hello World"


IO () is a monad (more on this later).
do is a procedural escape (i.e. it's not Functional).
So at the point of being practical, Haskell stops being Functional.



sum(range(10))
Functional vs Proceedural

More things change on the right than the left.




Evaluation Sample Sequence of Simple Loop: Functional vs Proceedural


run_tot => sum(items) => sum(range(10)) => sum([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) => 45

run_tot reduces to (i.e. ~ points towards ) the same thing all the time.
So, in a certain sense, what run_tot is never changes.

It's not that there are fewer moving parts.
It's that the parts that are there don't move.


So Why Care?
What Does Immutability Buy Us?


Functional Programming Run Through

More like a drive by,
worked from the bottom up.




x = my_first_thought(data_1)
y = something_better()
z = what_should_have_come_first(data_2)

solution_set = f(y, z)

#The Output Requested
print solution_set


The Anti-Pattern
Un-Pythonic Code

Because bad-bad programming makes me feel so good.


class powers(int):
    def __new__(self, num):
        return int.__new__(self, num)
    def __init__(self, num):
        self.p1 = pow(num, 1)
        self.p2 = pow(num, 2)
        self.p3 = pow(num, 3)
    def __add__(self, r_int):
        return self + r_int
    def state(self):
        print 'Base: %d, Square: %d, Cube: %d' % (
            self.p1, self.p2, self.p3)
        
p = powers(3)
p.state()
#Base: 3, Square: 9, Cube: 27
p.p1 = 10
p.state()
#Base: 10, Square: 9, Cube: 27


State changes aren't always updated (or in time).

The Pattern
a.k.a.
More Functional Code

Simple Steps to Better Code

Single return values.
Separation of side effects.



Good                                Bad

#Return a single value type:

def adder(x, y):                    def adder(x, y):
    return x + y                        if x and y:
	                                        return x + y
                                        else:
                                            return 'Need two ints'

#Isolate side effects

def updater(p_num):                 def update_pr(p_num):
    p_num.p2 = pow(num, 2)              p_num.p2 = pow(num, 2)
    p_num.p3 = pow(num, 3)              p_num.p3 = pow(num, 3)
                                        return (p_num.p2, p_num.p3)
t = (p_num.p2, p_num.p3)


Filters

Separating the wheat from the chaff.


def long_form_of_even(n):
    test = n % 2 == 0
    if test:
        return True
    else:
        return False

def even(n):
    return n % 2 == 0

to_ten = range(10)
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

even_to_ten = [t for t in to_ten if even(t)]
#[0, 2, 4, 6, 8]

even_to_ten = filter(even, to_ten)
#[0, 2, 4, 6, 8]

from itertools import compress
mask = [even(t) for t in to_ten]
#[True, False, True, False, True,
    False, True, False, True, False]
even_to_ten = list(compress(to_ten, mask))
#[0, 2, 4, 6, 8]


Lists

Some folks take notes, I make lists.


#Simple Map (list to list)

x = range(10)
map_1 = map(lambda x: x*x, x)
map_2 = [a*a for a in x]

#x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#map_1 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
#map_2 = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]


#Simple Reduce (list to scalar)

reduce_1 = sum(x)
#x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
#reduce_1 = 45

reduce_2 = 0
while x:
    reduce_2 += x.pop()
#x = []
#reduce_2 = 45


Map takes a list, returns a list.
Reduce takes a list, returns a scalar.

Monads

Writing a Monad tutorial.
The real 'Hello World' of the Haskell world.



Monads are wrappers...
or inverted wrappers if you want to get technical about it.

Simple Wrapper



def pre_post_pass_through(func):
    def whatever():
        print 'pre-process'
        func()
        print 'post-process'
    return whatever

@pre_post_pass_through
def base_function():
    print '\tMain Process'

base_function()

#pre-process
#	Main Process
#post-process


Round Trip

Being a simple little example...


def water_state(Celsius):
    '''Returns a string indicating water phase
    at provided temperature in Celsius.'''
    if Celsius <= 0:
        t = 'solid'
    elif Celsius < 100:
        t = 'liquid'
    else:
        t = 'gaseous'
    return t

def warmer(Celsius):
    '''Adds 10 degrees to Celsius, making it warmer.'''
    return 10.0 + Celsius

def report(Celsius):
    '''Returns text string reporting phase/temp.'''
    return 'Water is {} at {:.2f}C'.format(
            water_state(Celsius), Celsius)

def Celsius_wrapper(func):
    '''Round trip conversion from Fahrenheit to Celsius and back,
    executing wrapped function using units in Celsius.'''
    def wrapper(Fahrenheit):

        #conversion in (pre-process)
        Celsius = (Fahrenheit - 32.0) * 5.0/9.0
        print 'Fahrenheit in: ', round(Fahrenheit, 2)
        print report(Celsius)

        #Execution of wrapped function
        Celsius = func(Celsius)
        print '\tfunction call'

        #conversion out (post-process)
        Fahrenheit = Celsius * 9.0 /5.0 + 32.0
        print report(Celsius)
        print 'Fahrenheit out: ', round(Fahrenheit, 2)

        return Fahrenheit
    return wrapper

@Celsius_wrapper
def warmer_Fahrenheit(Fahrenheit):
    return warmer(Fahrenheit)

print 'Returned Value: %.2f' % warmer_Fahrenheit(Fahrenheit=25)
#Fahrenheit in:  25.0
#Water is solid at -3.89C
#	function call
#Water is liquid at 6.11C
#Fahrenheit out:  43.0
#Returned Value: 43.00

print 'Returned Value: %.2f' % warmer_Fahrenheit(Fahrenheit=25)
#Fahrenheit in:  25.0
#Water is solid at -3.89C
#	function call
#Water is liquid at 6.11C
#Fahrenheit out:  43.0
#Returned Value: 43.00


Monads

Monads were promised.

The Procedural Paradigm
Functional is the exception

@functional_programming
def python_code():
	do_stuff_functionally_for_a_change()


The Functional Paradigm
Procedural is the exception

@procedural_programming
def haskell_code():
	do_stuff_procedurally_for_a_change()
	

Monads are the escape hatch to the real world in functional programming.


Currying
Closures
and other
Cool Stuff

Currying

Is the process of partially pre-filling a function.


#Pre-filling with a function
curry_func = lambda y: map(lambda x: x*x, y)
print curry_func(range(5))
#[0, 1, 4, 9, 16]

#Pre-filling with data
curry_data = lambda y: map(y, range(5))
print curry_data(lambda x : x*x)
#[0, 1, 4, 9, 16]


Classes are functions attached to data.
Closures are data attached to functions.



In the End
Enlightenment


Eye of the Buddha
You know, because I promised some cool stuff at the end, there.


www.paufler.net

paufler.net@gmail.com

Terms of Service
© Copyright 2015 Brett Paufler