I would forget about package-level “partial”. That’s too much for the most basic syntax. In fact what I outline below already feels somewhat overwhelming. The package-level stuff could be utilities built on top of what we discuss here but they would not be an (indispensable) part of it.
How about using ` ?
One could transform (at AST post-processing stage) `rhs(a1, a2, ~, a3, k1=1, k2=~, k3=3) into a variant of lambda (only at AST level - for combining with |> later; post-optimization, only regular lambas would be passed to the compiler for bytecode production):
pipelambda lhs, in_pipeline=False: \
(
rhs(a1, a2, lhs, a3, k1=1, k2=lhs, k3=3)
if not in_pipeline
else (
lhs.__lcall__(rhs, a1, lhs, a3, k1=1, k2=lhs, k3=3)
if hasattr(lhs, "__lcall__")
else (
rhs.__rcall__(lhs, rhs, a1, lhs, a3, k1=1, k2=lhs, k3=3)
if hasattr(rhs, "__rcall__")
else rhs(a1, lhs, a3, k1=1, k2=lhs, k3=3)
)
)
)
The method invocation could use double `, i.e. ``. It would be simply ``method_name or ``method_name(...). It would translate to:
lambda lhs, in_pipeline=False: \
(
getattr(rhs, method_name)(a1, a2, lhs, a3, k1=1, k2=lhs, k3=3)
if not in_pipeline
else (
lhs.__lcall__(getattr(rhs, method_name), a1, lhs, a3, k1=1, k2=lhs, k3=3)
if hasattr(lhs, "__lcall__")
else (
rhs.__rcall__(lhs, getattr(rhs, method_name), a1, lhs, a3, k1=1, k2=lhs, k3=3)
if hasattr(rhs, "__rcall__")
else getattr(rhs, method_name)(a1, lhs, a3, k1=1, k2=lhs, k3=3)
)
)
)
My fancy example could now be written:
`pd.read_csv(...) |> ``query("A > B") |> `filter(items=["A"]) |> ``to_numpy |> ``flatten |> ``tolist |> `map(lambda x: x + 2) |> list |> np.array |> ``prod
|> could be made to accept raw callables (i.e. list instead of `list) or not. I think it’s fine to accept them and just transform raw callables into pipelambda when part of a pipeline. All could happen during AST post-processing.
Heavy but not too bad and this could satisfy everything we talked about, no? Split into components (new “partial” and the |> operator) and magic methods (__lcall__, __rcall__) for extensibility, terse syntax for syntactic convenience
Four new tokens `, ``, ~ and |> but a nice syntax without unexpected behaviors and not that much of a cognitive load IMHO.
What do you think?
PS. Better alternatives for backtick could be ! or \ (if we allowed it mid-line and made it mean line continuation only when actually used at line end).
!pd.read_csv(...) |> !!query("A > B") |> !filter(items=["A"]) |> !!to_numpy |> !!flatten |> !!tolist |> !map(lambda x: x + 2) |> list |> np.array |> !!prod
\pd.read_csv(...) |> \\query("A > B") |> \filter(items=["A"]) |> \\to_numpy |> \\flatten |> \\tolist |> \map(lambda x: x + 2) |> list |> np.array |> \\prod
Actually, I like ! / !! quite a lot - they naturally bring a lot of attention where attention is due.