[Python-ideas] Function composition (was no subject)
Terry Reedy
tjreedy at udel.edu
Sat May 9 17:20:53 CEST 2015
On 5/9/2015 6:19 AM, Andrew Barnert via Python-ideas wrote:
> I think there was, and still is. People keep coming up with abstract toy examples, but as soon as someone tries to give a good real example, it only makes sense with NumPy (Koos's) or with some syntax that Python doesn't have (yours), because to write them with actual Python functions would actually be ugly and verbose (my version of yours).
>
> I don't think that's a coincidence. You didn't write "map square" because you don't know how to think in Python, but because using compose profitably inherently implies not thinking in Python. (Except, maybe, in the case of NumPy... which is a different idiom.) Maybe someone has a bunch of obvious good use cases for compose that don't also require other functions, operators, or syntax we don't have, but so far, nobody's mentioned one.
I agree that @ is most likely to be usefull in numpy's restricted context.
A composition operator is usually defined by application: f at g(x) is
defined as f(g(x)). (I sure there are also axiomatic treatments.) It
is an optional syntactic abbreviation. It is most useful in a context
where there is one set of data objects, such as the real numbers, or one
set + arrays (vectors) defined on the one set; where all function are
univariate (or possible multivariate, but that can can be transformed to
univariate on vectors); *and* where parameter names are dummies like
'x', 'y', 'z', or '_'.
The last point is important. Abbreviating h(x) = f(g(x)) with h = f @ g
does not lose any information as 'x' is basically a placeholder (so get
rid of it). But parameter names are important in most practical
contexts, both for understanding a composition and for using it.
dev npv(transfers, discount):
'''Return the net present value of discounted transfers.
transfers: finite iterable of amounts at constant intervals
discount: fraction per interval
'''
divisor = 1 + discount
return sum(tranfer/divisor**time
for time, transfer in enumerate(transfers))
Even if one could replace the def statement with
npv = <some combination of @, sum, map, add, div, power, enumerate, ...>
with parameter names omitted, it would be harder to understand. Using
it would require the ability to infer argument types and order from the
composed expression.
I intentionally added a statement to calculate the common subexpression
prior to the return. I believe it would have to put back in the return
expression before converting.
--
Terry Jan Reedy
More information about the Python-ideas
mailing list