I find it interesting that the author uses AST rewriting to convert |. I’ve seen approaches in the past which just use a filler object (like foo | into | bar, into being the filler) which has a special __or__ method that does the piping.
foo | into | bar
I personally have experimented with a simple function just called pype() which also does some magic to avoid the need for lambda functions, see here.
EDIT: I did a quick writeup of my solution.
I always just reduced the value over the reverse of callables. It made higher order stuff, particularly data transforms, a lot easier to read. The code I used at my last job is open sourced here (the thread() function): https://github.com/curiosity/yunobuiltin/blob/develop/yunobuiltin.py#L444
We used pipeline() more than thread(), which takes only callables (no value) and returns a callables. It is functional composition with the order reversed such that the callables are applied from left to right.
The nice advantage of using the ast is that you can get much closer to Clojure’s -> and friends. By working from a compiler hook (the ast transformer), you wouldn’t need to use partial/rpartial/lambda as much. The downside is that it is quite a bit more complicated than a one liner.
That is very nice and clean, though I don’t really like lambdas in Python, they feel super verbose for what they are. I wish there was Haskell-style partial application.
I’d considered that approach as well, but I wanted to avoid needing a wrapper for the leftmost expression and I also wanted to avoid needing to partially apply functions.
Take the 3rd example from the post:
Users.find_all() | group_by_category(max=5) | print()
without transforming the ast that’d have to look something like:
pipe(Users.find_all()) | partial(group_by_category, max=5) | print
This reminds me of Coconut, which actually compiles to Python.
Oh, this is called uniform function call syntax (UFCS) in D and Nim. It does make code nicer to read in my experience.
Cytoolz’ curry has always served me well