# When programming functionally in Python

## FP is hot...

• Facebook uses FP on news feed
• C++11 and Java8 provides λ calculus
• Reacts.js - ClojureScript in FP
• Scala runs on JVM
• F# runs on .NET framework

## Some theories

• (196x) Domain theory
• (194x) Category theory
• (193x) Lambda calculus
• (192x) Combinatory logic
• (190x) Type theory
• ....

## Python supports FP...?

• Python documentation:
"FP HOWTO"
• Standard libraries:
• itertools
• functools
• operators
• Language supports:
• First class function
• Generator

## Before discussion...

What we do care is:

• Easy to develop
• Easy to maintain

=> Modularization?

## The most important thing

"Why Functional Programming Matters"

Functional programming languages provide two new kinds of glue — higher-order functions and lazy evaluationUsing these glues one can modularize programs in new and useful ways, ...

## Wrong value return?

``````s = '1 2 3'

t = ' '.join((s, '4'))
# t == '1 2 3 4'

L = ' '.join((s, '4')).split()
# L == ['1','2','3','4']

S = ' '.join((s, '4')).split().append('5')
# S is None
``````

## Generator failure?

``````>>> R3 = range(3)
>>> for i in R3:
...   print(i)
...
0
1
2
>>> for i in R3:
...   print(i)
...
0
1
2``````
``````>>> T = (0,1,2)
>>> G3 = (i for i in T)
>>> for i in G3:
...     print(i)
...
0
1
2
>>> for i in G3:
...     print(i)
...``````
``````def fibs():
yield 0
yield 1

def tail(G):
next(G)
yield from G

def zipWith(f, S, G):
for s,g in zip(S, G):
yield f(s,g)

``fibs = 0 : 1 : zipWith (+) fibs (tail fibs)``
``````import asyncio
import datetime

@asyncio.coroutine
def display_date(loop):
end_time = loop.time() + 5.0
while True:
print(datetime.datetime.now())
if (loop.time() + 1.0) >= end_time:
break
yield from asyncio.sleep(1)

loop = asyncio.get_event_loop()
loop.run_until_complete(display_date(loop))
loop.close()``````

## Broken closure?

``````>>> fs = [(λ x: x+y) for y in range(10)]
>>> fs[0](1)
10``````
``````> let fs = [(\x -> x+y) | y<-[0..9]]
> (fs !! 0) 1
1``````
``````>>> fs = ((λ x: x+y) for y in range(10))
>>> next(fs)(1)
1
>>> fs = map((λ y: λ x: x+y), range(10))
>>> next(fs)(1)
1``````
``````>>> fs = [(λ x: x+y) for y in range(10)]
>>> fs[0](1)
10``````
``````>>> fs = []
>>> for y in range(10):
...     fs.append((λ x:x+y))
...
>>> fs[0](1)
10``````

## Python doesn't provide...

1. Algebraic Data Type
2. Recursive Data Type
3. Parametric or generic
``````> data List a = Nil | Cons a (List a)
> :t Nil
Nil :: List a
> :t Cons 1 Nil
Cons 1 Nil :: Num a => List a``````
``````class List:
def __init__(self):
self._list= []
def cons(self, item):
assert isinstance(item, A)
return [item] + self._list``````
``````class L(type):
def __new__(tcls, cls):
def init(self, *a):
self._data = [a[0]]+a[1]._data if a else []

name = tcls.__name__+' '+cls.__name__
namespace = {'_cls': cls, '__init__': init}
wrapped_cls = type(name, (), namespace)
return wrapped_cls

A = int
LA = L(A)

def nil():
return LA()

def cons(a, b):
assert isinstance(a, LA._cls)
assert isinstance(b, LA)
return LA(a, b)
``````
``````data Tree a = Tip
| Node { value :: a,
left  :: Tree a,
right :: Tree a }``````
``````class TreeA(A, metaclass=Tree):
def get_value(self):
return self._node['value']
def get_left(self):
return self._node['left']
def get_right(self):
return self._node['right']``````
``````data Tree a = Tip | Node a (Tree a) (Tree a)

value :: Tree a -> a
left  :: Tree a -> Tree a
right :: Tree a -> Tree a``````
``````value (Node value left right) = value
left  (Node value left right) = left
right (Node value left right) = right
``````

## Pattern matching

``````data Tree a = Tip | Node a (Tree a) (Tree a)

value :: Tree a -> a
left  :: Tree a -> Tree a
right :: Tree a -> Tree a``````
``````value (Node value _ _) = value
left  (Node _ left _)  = left
right (Node _ _ right) = right
``````
``````def very_long_name(*lots_of_vars):
...
return state, the_important_value

#value = very_long_name(*lots_of_vars)[1]
_, value = very_long_name(*lots_of_vars)``````

## Type class

• Not "Class" of OO
• Another form of data abstraction
``````class Tree t where
nil   :: t a
node  :: a -> t a -> t a -> t a
value :: t a -> Maybe a
left  :: t a -> Maybe (t a)
right :: t a -> Maybe (t a)``````
``````data Tree a = Tip
| Node { value :: a,
left  :: Tree a,
right :: Tree a }``````
``````class Tree t where
nil   :: t a
node  :: a -> t a -> t a -> t a
value :: t a -> Maybe a
left  :: t a -> Maybe (t a)
right :: t a -> Maybe (t a)``````
``````data T a = I | N a (T a) (T a)
instance Tree T where
nil             = I
node  v l r     = N v l r
value I         = Nothing
value (N v l r) = Just v
left  I         = Nothing
left  (N v l r) = Just l
right I         = Nothing
right (N v l r) = Just r``````
``````class Tree t where
nil   :: t a
node  :: a -> t a -> t a -> t a
value :: t a -> Maybe a
left  :: t a -> Maybe (t a)
right :: t a -> Maybe (t a)``````
``````class Tree(metaclass=ABCMeta):
@staticmethod
@abstractmethod
def tree_nil():
...
@staticmethod
@abstractmethod
def tree_node(v, l, r):
...
@abstract_method
def get_value(self):
...
@abstract_method
def get_left(self):
...
@abstract_method
def get_right(self):
...``````

``````class Monad m where
return :: a -> m a
(>>=)  :: m a -> (a -> m b) -> m b``````
``````data Maybe a = Nothing | Just a
return a = Just a
(>>=) (Just a)  f = f a
(>>=) (Nothing) f = Nothing``````
``````data T a = I | N a I I
return a          = N a I I
(>>=) (N a _ _) f = f a
(>>=) I         f = I``````
``````divide y x = if y==0
then Nothing
else Just (x/y)

add y x = Just (x+y)``````
``````Just 1 >>= (add 3) >>= (divide 3)
-- Just 1.3333

Just 1 >>= (divide 0) >>= (add 1)
-- Nothing``````
``````fd = open('xxx','w')
fd.write('...')
fd.close()``````
``````do {
fd <- openFile "xxx" WriteMode;
hPutStr fd "...";
hClose fd;
}
-- :: IO a``````
``````fd = open('xxx', 'w')
try:
fd.write('...')
finally:
fd.close()``````
``````with open('xxx', 'w') as fd:
fd.write('...')``````
``````withFile "xxx" WriteMode (\fd -> do
hPutStr fd "...")``````

## Generic function

``````def fcn(a, b=None, *args, **kwargs): ...

def fcn(a, *, b=None, **kwargs): ...``````
``````def grab_image(path=None, stream=None, url=None):
r"""
>>> img = grab_image(path='my.png')

>>> import sys
>>> img = grab_image(stream=sys.stdin)

>>> img = grab_image(path='http://localhost')
"""
...
return img``````
``````grab_image(path='my.png',
stream=sys.stdin,
url='http://localhost')``````
``````@singledispatch
def grab_image(src):
raise NotImplementedError

@grab_image.register(str)
def _(path):
...
return img

@grab_image.register(Stream)
def _(stream):
...
return img
``````
``````class Stream(metaclass=ABCMeta):
@classmethod
def __subclasshook__(cls, C):

## Not on the same page

imperative ⇔ declarative

interpreted ⇔ compiled

dynamic typed ⇔ static typed

## Python does support FP

...does support...

## References

• http://en.wikipedia.org/wiki/Abstract_type

• http://en.wikipedia.org/wiki/Transactional_memory#Motivation

• https://docs.python.org/3/library/itertools.html#itertools.tee

• https://docs.python.org/3/tutorial/classes.html#random-remarks

• http://neopythonic.blogspot.com/2009/04/final-words-on-tail-calls.html

• http://pyos.github.io/dg/

• http://doc.ponyorm.com/

• https://github.com/lihaoyi/macropy

## References

• PEP 484 -- Type Hints
• PEP 343 -- The "with" Statement
• PEP 342 -- Coroutines via Enhanced Generators
• PEP 380 -- Syntax for Delegating to a Subgenerator
• PEP 3102 -- Keyword-Only Arguments
• Functional Programming HOWTO
• Why functional programming matters

By Apua A.Aa

# Python and FP

From my note about how FP in Python. The slide is used to the regular talk at PyConAPAC 2015.

• 1,169