[Python-ideas] Function composition (was no subject)

Chris Angelico rosuav at gmail.com
Sun May 10 11:25:31 CEST 2015


On Sun, May 10, 2015 at 2:58 PM, Douglas La Rocca
<larocca at abiresearch.com> wrote:
> (Newcomer here.)

Welcome to Bikeshed Central! Here, we take a plausible idea and fiddle
around with all the little detaily bits :)

> For me, the ideal infix operator would simply be a space, with the composition wrapped in parentheses. So e.g.
>
>>>> (list str sorted)(range(10))
> [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ',', ',', ',', ',', ',', ',', ',', ',', ',', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '[', ']']
>
> I might be overlooking something, but it seems to me this would work with existing syntax and semantics and wouldn't conflict with anything else like operator overloading would.
>

One of the problems with using mere whitespace is that it's very easy
to do accidentally. There's already places where this can happen, for
instance:

strings = [
    "String one",
    "String two is a bit longer"
    "String three",
    "String four"
]

How many strings are there in my list? Clearly the programmer's
intention is to have four, but that's not what ends up happening. (Now
imagine there are actually hundreds of strings, and one gets selected
at random every time you do something. Have fun figuring out why, just
occasionally, it prints out two messages instead of one. For bonus
points, figure that out when there are two or three such bugs in the
list, so it's not always the exact same pair that come out together.)

At the moment, we can safely build up a list of functions like this:

funcs = [
    list,
    str,
    sorted,
]

because omitting a comma will produce an instant SyntaxError.

Python currently is pretty good at detecting problems in source code.
(Not all languages are, as you'll know as soon as you run into one of
those "oops I left out a semicolon and my JavaScript function does
something slightly different" bugs.) Part of that comes from having a
fairly simple set of rules governing syntax, such that any deviation
results in a simple and quick error *at or very near to* the place
where the error occurs. You won't, for instance, get an error at the
bottom of a file saying "Unmatched '{' or missing '}'", leaving you to
dig through your code to figure out exactly where the problem was. At
worst, you get an error on the immediately-following line of code:

def func1():
    value = x * (y + z # oops, forgot the close parens
    print(value) # boom, SyntaxError on this line

But if "function function" meant composition, this would actually be
legal, and you'd get an error rather further down. If you're lucky,
this is the end of this function, and the "def" keyword trips the
error; but otherwise, this would be validly parsed as "compose z and
print into a function, then call that with value", and we're still
looking for a close parens.

So I would strongly suggest having some sort of operator in between.
Okay. Can I just say something crazy? (Hans: I love crazy!) How about
using a comma?

>>> (fn1, fn2, fn3, ...)('string to be piped')

Currently, this produces a runtime TypeError: 'tuple' object is not
callable, but I could easily define my own callable subclass of tuple.

>>> class functuple(tuple):
...     def __call__(self, arg):
...         for func in self: arg = func(arg)
...         return arg
...
>>> f = functuple((fn1,fn2))
>>> f("this is a test")

(Use whatever semantics you like for handling multiple arguments. I'm
not getting into that part of the debate, as I have no idea how
function composition ought to work in the face of *args and **kwargs.)

The syntax is reasonably clean, and it actually doesn't require many
changes - just making tuples callable in some logical fashion. No new
syntax needed, and it's an already-known light-weight way to pack up a
bunch of things into one object. Does it make sense to do this?

ChrisA


More information about the Python-ideas mailing list