Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python infix forward pipe

I'm trying to implement a forward pipe functionality, like bash's | or R's recent %>%. I've seen this implementation https://mdk.fr/blog/pipe-infix-syntax-for-python.html, but this requires that we define in advance all the functions that might work with the pipe. In going for something completely general, here's what I've thought of so far.

This function applies its first argument to its second (a function)

def function_application(a,b):
    return b(a)

So for example, if we have a squaring function

def sq(s):
    return s**2

we could invoke that function in this cumbersome way function_application(5,sq). To get a step closer to a forward pipe, we want to use function_application with infix notation.

Drawing from this, we can define an Infix class so we can wrap functions in special characters such as |.

class Infix:
    def __init__(self, function):
        self.function = function
    def __ror__(self, other):
        return Infix(lambda x, self=self, other=other: self.function(other, x))
    def __or__(self, other):
        return self.function(other)

Now we can define our pipe which is simply the infix version of the function function_application,

p = Infix(function_application)

So we can do things like this

5 |p| sq
25

or

[1,2,3,8] |p| sum |p| sq
196

After that long-winded explanation, my question is if there is any way to override the limitations on valid function names. Here, I've named the pipe p, but is it possible to overload a non-alphanumeric character? Can I name a function > so my pipe is |>|?

like image 830
keegan Avatar asked Feb 10 '23 14:02

keegan


1 Answers

Quick answer:

You can't really use |>| in python, at the bare minimum you need | * > * | where * needs to be a identifier, number, string, or another expression.

Long answer:

Every line is a statement (simple or compound), a stmt can be a couple of things, among them an expression, an expression is the only construct that allows the use of or operator | and greater than comparison > (or all operators and comparisons for that matter < > <= >= | ^ & >> << - + % / //), every expression needs a left hand side and a right hand side, ultimatelly being in the form lhs op rhs, both left and right hand side could be another expression, but the exit case is the use of an primary (with the exception of unnary -, ~ and + that need just a rhs), the primary will boil down to an identifier, number or string, so, at the end of the day you are required to have an identifier [a-zA-Z_][a-zA-Z_0-9]* along side a |.

Have you considered a different approach, like one class that override the or operator instead of a infix class? I have a tiny library that does piping, might interest you

For reference, here is the full grammar:

https://docs.python.org/2/reference/grammar.html

like image 101
Augusto Hack Avatar answered Feb 20 '23 12:02

Augusto Hack