From c2f10bfcf7e9e72aef6f8c8c0c0b8703189176b5 Mon Sep 17 00:00:00 2001 From: Erik Welch Date: Wed, 19 Mar 2014 18:48:01 -0400 Subject: [PATCH] Make `Curry` a class and `curry` a function. Working on #147. 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. --- toolz/functoolz/core.py | 103 +++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 49 deletions(-) diff --git a/toolz/functoolz/core.py b/toolz/functoolz/core.py index 657e0d7f..747e21e7 100644 --- a/toolz/functoolz/core.py +++ b/toolz/functoolz/core.py @@ -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 @@ -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