Skip to content

Commit

Permalink
Make Curry a class and curry a function. Working on pytoolz#147. …
Browse files Browse the repository at this point in the history
…Not finished.

This includes a significant change to curried objects: they never return
`functools.partial` objects.  Instead, `Curry` *uses* `partial`.  This
is because `partial` behaves differently.  Let me illustrate:

`curry(lambda x, y=0: x+y)(y=2)` is valid.
`partial(lambda x, y=0: x+y)(y=2)` returns a TypeError.

More documentation and tests are needed.  PRs (and comments) are welcome.
  • Loading branch information
eriknw committed Mar 19, 2014
1 parent 2c1e262 commit c2f10bf
Showing 1 changed file with 54 additions and 49 deletions.
103 changes: 54 additions & 49 deletions toolz/functoolz/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,45 @@ def _num_required_args(func):
return None


class curry(object):
class Curry(object):
def __init__(self, func, *args, **kwargs):
if not callable(func):
raise TypeError("Input must be callable")

if kwargs:
self.partial = partial(func, *args, **kwargs)
else:
self.partial = partial(func, *args)
self.func = self.partial.func
self.args = self.partial.args
self.keywords = self.partial.keywords
self.__doc__ = self.func.__doc__
try:
self.func_name = self.func.func_name
except AttributeError:
pass

def __str__(self):
return str(self.func)

def __repr__(self):
return repr(self.func)

def __call__(self, *args, **kwargs):
try:
return self.partial(*args, **kwargs)
except TypeError:
required_args = _num_required_args(self.func)

# If there was a genuine TypeError
if (required_args is not None and
len(args) + len(self.args) >= required_args):
raise

return curry(self.partial, *args, **kwargs)


def curry(func, *args, **kwargs):
""" Curry a callable function
Enables partial application of arguments through calling a function with an
Expand Down Expand Up @@ -133,54 +171,21 @@ class curry(object):
toolz.curried - namespace of curried functions
http://toolz.readthedocs.org/en/latest/curry.html
"""
def __init__(self, func, *args, **kwargs):
if not callable(func):
raise TypeError("Input must be callable")

self.func = func
self.args = args
self.keywords = kwargs if kwargs else None
self.__doc__ = self.func.__doc__
try:
self.func_name = self.func.func_name
except AttributeError:
pass

def __str__(self):
return str(self.func)

def __repr__(self):
return repr(self.func)

def __call__(self, *args, **_kwargs):
args = self.args + args
if _kwargs:
kwargs = {}
if self.keywords:
kwargs.update(self.keywords)
kwargs.update(_kwargs)
elif self.keywords:
kwargs = self.keywords
else:
kwargs = {}

try:
return self.func(*args, **kwargs)
except TypeError:
required_args = _num_required_args(self.func)

# If there was a genuine TypeError
if required_args is not None and len(args) >= required_args:
raise

# If we only need one more argument
if (required_args is not None and required_args - len(args) == 1):
if kwargs:
return partial(self.func, *args, **kwargs)
else:
return partial(self.func, *args)

return curry(self.func, *args, **kwargs)
if not callable(func):
raise TypeError("Input must be callable")

# Curry- or functools.partial-like object? Unpack and merge arguments
if (hasattr(func, 'func') and hasattr(func, 'args')
and hasattr(func, 'keywords')):
_kwargs = {}
if func.keywords:
_kwargs.update(func.keywords)
_kwargs.update(kwargs)
kwargs = _kwargs
args = func.args + args
func = func.func

return Curry(func, *args, **kwargs)


@curry
Expand Down

0 comments on commit c2f10bf

Please sign in to comment.