import inspect from minitest import case, tests from functools import wraps, partial def makepartial(fn): """ This function is a decorator which allows functions with a fixed number of arguments gracefully to be transformed into partial functions given too few arguments to execute. For instance, if `add(1, 2)` returns 3, `add(1)` should return a function `f` such that `f(2)` returns 3. """ @wraps(fn) def maybepartial(*args, **kwargs): """ Either calls and returns the decorated function, or -- if we don't have enough arguments -- creates a partial function with the given parameters prefilled. """ sig = inspect.signature(fn) if len(args) + len(kwargs) == len(sig.parameters): return fn(*args, **kwargs) return makepartial(partial(fn, *args, **kwargs)) return maybepartial @makepartial def add(a, b): return a + b # Tests: @case def test_correctness(t): t.check_equal(add(1, 2), 3) @case def test_currying(t): t.check_equal(add(1)(2), add(1, 2)) t.check_equal(add()(1)(2), add(1)(2)) if tests: tests.run_all()