KEMBAR78
Functional Pattern Matching on Python | PDF
FUNCTIONAL
PATTERN
MATCHING
DAKER PINHEIRO
DAKER FERNANDES
PINHEIRO
UFPE
INDT RECIFE
WEBKIT (NIX)
QT, KDE, ...
C++, C, PYTHON, JAVASCRIPT, PROLOG, ...
PYTHON SINCE 2009
PROLOG
botname("Chatty").
chatterbot(['hello'], ['Hello', ',', 'my', 'name', 'is', B]) :-
botname(B).
chatterbot(['how', 'much', 'is' | Expr], ['It', 'is'], Result) :-
eval_expression(Expr, Result).
chatterbot(['my', 'name', 'is', Name], ['Nice', 'to', 'meet', 'you']) :-
assert(name(Name)).
PATTERN
MATCHING
HASKELL
fib :: Int -> Int
fib 1 = 1;
fib n = n * fib n - 1;
HASKELL
count :: [n] -> n -> n
count (n:tail) n = 1 + count tail n;
count (k:tail) n = count tail n;
count [] _ = 0;
ERLANG
greet(male, Name) ->
io:format("Hello, Mr. ~s!", [Name]);
greet(female, Name) ->
io:format("Hello, Mrs. ~s!", [Name]);
greet(_, Name) ->
io:format("Hello, ~s!", [Name]).
PYTHON!!
@patterns
def fib():
if 1: 1
if n: n * fib(n - 1)
DISCLAIMER
PIPINSTALLPATTERNS
... OR CLONE IT.
HOW IT WORKS?
@patterns
if function():
if RULE1: EXPRESSION1
if RULE2: EXPRESSION2
...
if function(*args):
if match(args, RULE1):
ASSIGNS1
return EXPRESSION1
elif match(args, RULE2):
ASSIGNS2
return EXPRESSION2
else:
raise Mismatch()
PYTHON
@patterns
def fib():
if 1: 1
if n: n * fib(n - 1)
def fib(n):
if n == 1:
return 1
else:
return n * fib(n - 1)
TREE
@patterns
def depth():
if ('Tree', _, left, right): 1 + max(depth(left), depth(right))
if None: 0
def depth(tree):
if tree:
return max(depth(tree[2]), depth(tree[3]))
else:
return 0
CHATTERBOT
botname = "Chatty"
@patterns
def answer():
if ['hello']:
"Hello, my name is %s" % botname
if ['hello', 'my', 'name', 'is', name]:
"Hello, %s!" % name.capitalize()
if ['how', 'much', 'is'] + expr:
"It is %d" % eval(' '.join(expr))
if ['bye']:
"Good bye!"
CHATTERBOT
botname = "Chatty"
def answer(message):
if message == ['hello']:
return "Hello, my name is %s" % botname
elif message[:-1] == ['hello', 'my', 'name', 'is']:
return "Hello, %s" % message[-1].capitalize()
elif message[:3] == ['how', 'much', 'is']:
return "It is %d" % eval(' '.join(expr))
elif message == ['bye']:
return "Good bye!"
else:
raise Mismatch()
BREATH DEEP,
SEEK PEACE
PATTERNS
import sys, inspect, ast, re
from ast import *
def patterns(func):
empty_argspec = inspect.ArgSpec(args=[], varargs=None,
keywords=None, defaults=None)
assert inspect.getargspec(func) == empty_argspec
# TODO: make it not as weird and dirty
func.__globals__['Mismatch'] = Mismatch
tree = get_ast(func)
transform_function(tree.body[0])
return compile_func(func, tree)
GET_AST
def get_ast(func):
# Get function source
source = inspect.getsource(func)
# Fix extra indent if present
spaces = re_find(r'^s+', source)
if spaces:
source = re.sub(r'(^|n)' + spaces, 'n', source)
return ast.parse(source, func_file(func), 'single')
TRANSFORM_FUNCTION
def transform_function(func_tree):
assert all(isinstance(t, If) for t in func_tree.body), 
'Patterns function should only have if statements'
# Adjust arglist and decorators
func_tree.args.args.append(Name(ctx=Param(), id='value'))
func_tree.decorator_list = []
...
TRANSFORM_FUNCTION
...
for test in func_tree.body:
cond = test.test
...
TRANSFORM_FUNCTION
...
if isinstance(cond, (Num, Str, List, Tuple)) and not has_vars(cond):
test.test = make_eq(N('value'), cond)
if isinstance(cond, (Num, Str, Name, Compare, List,
Tuple, Dict, BinOp)):
tests, assigns = destruct_to_tests_and_assigns(N('value'), cond)
test.test = BoolOp(op=And(), values=tests) if tests else V(1)
test.body = assigns + test.body
else:
raise TypeError("Don't know how to match %s" % ...)
...
TRANSFORM_FUNCTION
...
if isinstance(cond, (Num, Str, List, Tuple)) and not has_vars(cond):
test.test = make_eq(N('value'), cond)
if isinstance(cond, (Num, Str, Name, Compare, List,
Tuple, Dict, BinOp)):
tests, assigns = destruct_to_tests_and_assigns(N('value'), cond)
test.test = BoolOp(op=And(), values=tests) if tests else V(1)
test.body = assigns + test.body
else:
raise TypeError("Don't know how to match %s" % ...)
...
TRANSFORM_FUNCTION
...
func_tree.body = map(wrap_tail_expr, func_tree.body)
func_tree.body.append(
Raise(type=N('Mismatch'), inst=None, tback=None))
TESTS_AND_ASSIGNS
def destruct_to_tests_and_assigns(topic, pattern):
if isinstance(pattern, (Num, Str)):
return [make_eq(topic, pattern)], []
elif isinstance(pattern, Name):
return [], [make_assign(pattern.id, topic)]
elif isinstance(pattern, Compare) and 
len(pattern.ops) == 1 and isinstance(pattern.ops[0], Is):
return [make_call('isinstance', topic, pattern.comparators[0])], 
[make_assign(pattern.left.id, topic)]
...
A NEW PEP? :-)
Q & A
DAKER FERNANDES PINHEIRO
HTTP://CODECEREAL.BLOGSPOT.COM

Functional Pattern Matching on Python