I’ve never been able to remember whether (f@g)(x) means f(g(x)) or g(f(x)). That pretty much kills the idea for me.
On Sun, May 24, 2020 at 11:10 Alex Hall <alex.moj...@gmail.com> wrote: > On Sun, May 24, 2020 at 6:56 PM David Mertz <me...@gnosis.cx> wrote: > >> On Sun, May 24, 2020 at 11:21 AM Steven D'Aprano <st...@pearwood.info> >> wrote: >> >>> > But how would you go about getting a .__matmul__ attribute onto all >>> > functions. For ones you write yourselves, you could decorate them at >>> > definition. What about all the other functions though. >>> >>> As a functional programming fan, you might not know about this, but we >>> object oriented programming people have this concept called >>> "inheritance" where we would add the attribute to FunctionType once, and >>> just like magic every function would support it! >>> >> >> I think I heard of that inheritance thing somewhere! :-) >> >> My thought is really that there's no way we're going to get .__matmul__ >> added (with the meaning "compose") to FunctionType. So I was thinking >> about what one could do with a separate module. I.e. something that could >> live on PyPI... before, perhaps someday, being added to standard library. >> >> Is there an evil way to monkey patch FunctionType? I.e. without actually >> recompiling a fork of the interpreter. Alex' forbiddenfruit.curse is >> pretty cool looking. But it does require explicitly cursing each >> individual function that might be composed. >> > > forbiddenfruit patches the type, look again. I used a loop because there > isn't just one FunctionType: `type(compose) != type(len)`. > > >> Alex Hall: >> >>> But seriously, I don't see that much point to this idea. It's just >>> slightly more concise while not being particularly readable or beginner >>> friendly. >>> >>> sorted(paths, key=len @ str) >>> sorted(paths, key=lambda p: len(str(p))) >>> >> >> I think once you compose three or more functions, lambda starts to look >> pretty bad. >> > > It does? How? It sounds like the problem isn't the syntax for lambda, but > just function composition in general, maybe because of too many > parentheses. Maybe you'd like to change: > > foo = bar(len(str(path))) > > into > > foo = (bar @ len @ str)(path) > > In any case, it's rare to be able to simply compose three unary functions, > e.g. `sorted(paths, key=bar @ len @ str)`. Usually you need to do something > slightly more complicated, e.g. a second argument somewhere, and if you > throw in functools.partial I think that's easily worse. > > >> This is only slightly mitigated by one of those proposals for "a more >> concise lambda" that occur intermittently. And if you actually save a >> composed function under a meaningful name for later use, that much more so. >> > > Why is: > > my_op = bar @ len @ str > > even more of an improvement over: > > my_op = lambda p: bar(len(str(p))) > > than @ is an improvement in the previous examples? If the answer is that > you're not supposed to assign lambdas, I think the same logic should > dictate not assigning compositions. > > In fact, the reason to not assign lambdas is for better tracebacks, > and now that I think of it, function composition would completely destroy > tracebacks. For example: > > ``` > for function in [ > lambda p: str(len(p)), > str @ len, > ]: > try: > function(3) > except: > traceback.print_exc() > > ``` > > Output: > > ``` > Traceback (most recent call last): > File "main.py", line 15, in <module> > function(3) > File "main.py", line 11, in <lambda> > lambda p: str(len(p)), > TypeError: object of type 'int' has no len() > Traceback (most recent call last): > File "main.py", line 15, in <module> > function(3) > File "main.py", line 5, in <lambda> > return lambda *args, **kwargs: self(other(*args, **kwargs)) > TypeError: object of type 'int' has no len() > ``` > > https://repl.it/@alexmojaki/LeanEnergeticPublisher-1 > > So based on that I'm strongly against this kind of proposal without > syntactic support. > > I've been playing more lately with R Tidyverse. It's pipe with currying >> of first argument is actually really nice. The pipe operator is god-awful >> ugly. But other than that it works nicely. For example: >> >> iris %>% group_by(Species) %>% summarize_if(is.numeric, mean) %>% >> ungroup() %>% gather(measure, value, -Species) %>% arrange(value) >> >> >> It's not abstract composition since it always starts with a concrete >> object the several operations work on. But it is some of the same feel. >> > > https://github.com/0101/pipetools > _______________________________________________ > Python-ideas mailing list -- python-ideas@python.org > To unsubscribe send an email to python-ideas-le...@python.org > https://mail.python.org/mailman3/lists/python-ideas.python.org/ > Message archived at > https://mail.python.org/archives/list/python-ideas@python.org/message/JURJ5YTZ2ESEBVZYNNJ4RDSCEMH5RPB3/ > Code of Conduct: http://python.org/psf/codeofconduct/ > -- --Guido (mobile)
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-le...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/6TDH6PH2R5U4CM4QDMSLOGORT73F6BNC/ Code of Conduct: http://python.org/psf/codeofconduct/