[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