Created
June 27, 2013 04:10
-
-
Save badocelot/5873884 to your computer and use it in GitHub Desktop.
Functional-paradigm Python decorators.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Functional-paradigm decorators | |
def curried (function): | |
argc = function.__code__.co_argcount | |
# Subtract the defaults from the minimum arity | |
if function.__defaults__: | |
argc -= len(function.__defaults__) | |
# Pointless to curry a function that can take no arguments | |
if argc == 0: | |
return function | |
from functools import partial | |
def func (*args): | |
if len(args) >= argc: | |
return function(*args) | |
else: | |
return partial(func, *args) | |
func.__doc__ = function.__doc__ | |
return func | |
def lazy (function): | |
def lazy_func(*args, **kwargs): | |
def real_func (): | |
return function(*args, **kwargs) | |
return real_func | |
lazy_func.__doc__ = function.__doc__ | |
return lazy_func | |
# Basically the Y-combinator as a decorator | |
def selfref (function): | |
def func (*args, **kwargs): | |
return function(func, *args, **kwargs) | |
func.__doc__ = function.__doc__ | |
return func | |
def trampolined (function): | |
def func (*args, **kwargs): | |
result = function(*args, **kwargs) | |
while callable(result): | |
result = result() | |
return result | |
func.__doc__ = function.__doc__ | |
return func | |
# Equivalent to: | |
# @trampolined | |
# @selfref | |
# @lazy | |
def tail_recursive (function): | |
return trampolined(selfref(lazy(function))) | |
# Curried and tail-recursive: a fully functional-paradigm function. | |
# | |
# Unfortunately I don't think this can be done as a combination of decorators, | |
# since @curried needs access to the base function to determine arity but tail | |
# call optimization requires the @tail_recursive version to ultimately be the | |
# one called. I tried rewriting @curried to take a decorator argument and call | |
# the decorated function but since @selfref changes the effective arity of the | |
# function by one, that's still no good. | |
def functional(function): | |
trfunc = tail_recursive(function) | |
argc = function.__code__.co_argcount | |
# Subtract the defaults from the minimum arity | |
if function.__defaults__: | |
argc -= len(function.__defaults__) | |
# Pointless to curry a function that can take no arguments | |
if argc == 0: | |
return trfunc | |
from functools import partial | |
def func (*args): | |
if len(args) >= argc - 1: | |
return trfunc(*args) | |
else: | |
return partial(func, *args) | |
func.__doc__ = function.__doc__ | |
return func | |
# Examples | |
def fact(n): | |
"""Factorial function written in tail-recursive style.""" | |
@tail_recursive | |
def inner_fact (self, acc, x): | |
if x > 1: | |
return self(acc * x, x - 1) | |
else: | |
return acc | |
return inner_fact(1, n) | |
@curried | |
def reduce (function, sequence, initial=None): | |
"""reduce (function, sequence[, initial=None]) | |
Curried wrapper for functools.reduce | |
""" | |
from functools import reduce as _reduce | |
if initial is not None: | |
return _reduce(function, sequence, initial) | |
return _reduce(function, sequence) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment