FAQ === * **Q. Python already includes a partial application class in the standard library's functools module; why not just use that?** There are two major reasons that I felt the need to write a new implementation of partial function application for this module. First, use of the function composition operators provided by the :py:class:`~pointfree.pointfree` decorator requires cooperation between the partial application mechanism and the implementation of overloaded operators; the result of a partial application must be an object which defines the necessary operators, so at the very least I would need to wrap :py:func:`functools.partial` anyway. (And that in itself would not be easy, because :py:func:`functools.partial` does not provide a way to test whether enough arguments have been provided to call the underlying function.) The second reason is a subjective matter of taste. The standard library's :py:func:`~functools.partial` requires explicit creation of a new object every time you wish to perform partial application and then a separate call in order to actually invoke the underlying function, and this is more verbose and (in my opinion) less elegant than I would like. For a contrived example:: >>> from functools import partial >>> def add_thrice(a, b, c): ... return a + b + c >>> plusone = partial(add_thrice, 1) >>> plusone(2, 3) 6 >>> plusthree = partial(plusone, 2) >>> plusthree(3) 6 In contrast, pointfree's :py:class:`~pointfree.partial` decorator lets you perform partial application with the same syntax as "full" application:: >>> from pointfree import partial >>> @partial ... def add_thrice(a, b, c): ... return a + b + c >>> plusone = add_thrice(1) >>> plusone(2, 3) 6 >>> plusthree = plusone(2) >>> plusthree(3) 6 There are also several minor ways in which :py:func:`functools.partial` is not ideal for supporting the pointfree style. If you have a function of two arguments and you specify the first as a keyword argument, you cannot then specify the second positionally in a subsequent application; this would prevent such a partially-applied function from being composed with other functions:: >>> from functools import partial >>> def add(a, b): ... return a + b >>> p = partial(add, a=1) >>> p(2) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... TypeError: add() got multiple values for keyword argument 'a' Whereas you can do this with pointfree, due to its slightly different semantics for positional argument application (which is fully described in the :ref:`module_reference`):: >>> from pointfree import partial >>> @partial ... def add(a, b): ... return a + b >>> p = add(a=1) >>> p(2) 3 Also, with the standard library's partial class you don't see errors immediately when you apply invalid positional or keyword arguments; the exception is only raised when you later ``__call__`` the partial object:: >>> from functools import partial >>> def add(a, b): ... return a + b >>> p = partial(add, c=3) # No error is raised yet >>> q = partial(p, 1) # Still no error >>> q(2) # Now we get an error! Traceback (most recent call last): ... TypeError: add() got an unexpected keyword argument 'c' But with pointfree's partial application, the error is raised immediately:: >>> from pointfree import partial >>> @partial ... def add(a, b): ... return a + b >>> p = add(c=3) Traceback (most recent call last): ... TypeError: add() got an unexpected keyword argument 'c' * **Q. Are there any disadvantages to pointfree's partial application style?** Because Python does not currently expose built-in functions for introspection, the pure-Python :py:class:`pointfree.partial` wrapper does not work with built-in functions. Also, with the pointfree implementation of partial application you cannot specify optional positional arguments in *multiple* applications, because evaluation will occur automatically as soon as enough arguments have been specified. So, for instance, with :py:func:`functools.partial`:: >>> from functools import partial >>> def add_all(*argv): ... return sum(argv) >>> f = partial(add_all, 1, 2) >>> g = partial(f, 3, 4) >>> g(5) 15 Whereas with pointfree, the function would be evaluated as soon as it has been supplied any arguments:: >>> from pointfree import partial >>> partial(add_all)(1, 2) # evaluated immediately 3 Despite these limitations, I prefer the brevity of the pointfree implementation (which is of course why I wrote it). Naturally, your mileage may vary.