[Python-ideas] Partial operator (and 'third-party methods' and 'piping') [was Re: Function composition (was no subject)]

Koos Zevenhoven koos.zevenhoven at aalto.fi
Sun May 10 23:41:59 CEST 2015


Hi Gregory,

Did you look at the new version carefully? If I understand the problem 
you are describing (mentioned also by Steven), my previous version had 
that issue, but the new one does not. That is why I added examples with 
callable arguments :).

-- Koos


On 11.5.2015 0:23, Gregory Salvan wrote:
> In my opinion, this syntax make problems when your arguments are 
> functions/callables.
> And if you code in a functionnal paradigm it is quite common to inject 
> functions in arguments otherwise how would you do polymorphism ?
>
> The only way I see to distinguish cases is to have tuples, but syntax 
> is quite strange.
>
> instead of : arg->eggs(b)->spam(c)
> my_partial = (arg, b)->eggs->(c, )->spam
>
> Then how would you call my_partial ?
> For example, if you have:
> def eggs(a, b, c)...
> def spam(d, e)...
>
> my_partial(c, e) or my_partial(c)(e) ?
>
>
>
> 2015-05-10 22:06 GMT+02:00 Koos Zevenhoven <koos.zevenhoven at aalto.fi 
> <mailto:koos.zevenhoven at aalto.fi>>:
>
>     Reading the recent emails in the function composition thread
>     started by Ivan, I realized that my below sketch for a composition
>     operator would be better if it did not actually do function
>     composition ;). Instead, -> would be quite powerful as 'just' a
>     partial operator -- perhaps even more powerful, as I demonstrate
>     below. However, this is not an argument against @ composition,
>     which might in fact play together with this quite nicely.
>
>     This allows some nice things with multi-argument functions too.
>
>     I realize that it may be unlikely that a new operator would be
>     added, but here it is anyway, as food for thought.  (With an
>     existing operator, I suspect it would be even less likely, because
>     of precedence rules : )
>
>     So, -> would be an operator with a precedence similar to
>     .attribute access (but lower than .attribute):
>
>      # The simple definition of what it does:
>      arg->func   # equivalent to functools.partial(func, arg)
>
>     This would allow for instance:
>      arg -> spam() -> cheese(kind = 'gouda') -> eggs()
>
>     which would be equivalent to eggs(cheese(spam(arg), kind = 'gouda'))
>
>     Or even together together with the proposed @ composition:
>      rms = root @ mean @ square->map     # for an iterable non-numpy
>     argument
>
>     And here's something I find quite interesting. Together with
>     @singledispatch from 3.4 (or possibly an enhanced version using
>     type annotations in the future?), one could add 'third-party
>     methods' to classes in other libraries without monkey patching. A
>     dummy example:
>
>     from numpy import array
>     my_list = [1,2,3]
>     my_array = array(my_list)
>     my_mean = my_array.mean()  # This currently works in numpy
>
>     from rmslib import rms
>     my_rms = my_array->rms()  # efficient rms for numpy arrays
>     my_other_rms = my_list->rms()  # rms that works on any iterable
>
>     One would be able to distinguish between calls to methods and
>     'third-party methods' based on whether . or -> is used for
>     accessing them, which I think is a good thing. Also, third-party
>     methods would be less likely to mutate the object, just like
>     func(obj) is less likely to mutate obj than obj.method().
>
>     See more examples below. I converted my examples from last night
>     to this IMO better version, because at least some of them would
>     still be relevant.
>
>     On 10.5.2015 2:07, Koos Zevenhoven wrote:
>
>         On 10.5.2015 1:03, Gregory Salvan wrote:
>
>             Nobody convinced by arrow operator ?
>
>             like: arg -> spam -> eggs -> cheese
>             or cheese <- eggs <- spam <- arg
>
>
>
>         I like | a lot because of the pipe analogy. However, having a
>         new operator for this could solve some issues about operator
>         precedence.
>
>         Today, I sketched one possible version that would use a new ..
>         operator. I'll explain what it would do (but with your ->
>         instead of my ..)
>
>         Here, the operator (.. or ->) would have a higher precedence
>         than function calls () but a lower precedence than attribute
>         access (obj.attr).
>
>         First, with single-argument functions spam, eggs and cheese,
>         and a non-function arg:
>
>         arg->eggs->spam->cheese()   # equivalent to
>         cheese(spam(eggs(arg)))
>
>
>     With -> as a partial operator, this would instead be:
>
>     arg->eggs()->spam()->cheese()     # equivalent to
>     cheese(spam(eggs(arg)))
>
>         eggs->spam->cheese  # equivalent to lambda arg:
>         cheese(spam(eggs(arg)))
>
>
>     With -> as a partial operator this could be:
>
>     lambda arg: arg->eggs()->spam()->cheese()
>
>
>         Then, if spam and eggs both took two arguments; eggs(arg1,
>         arg2), spam(arg1, arg2)
>
>         arg->eggs   # equivalent to partial(eggs, arg)
>         eggs->spam(a, b, c)   # equivalent to spam(eggs(a, b), c)
>
>
>     With -> as a partial operator, the first one would work, and the
>     second would become:
>
>     eggs(a,b)->spam(c)     # equivalent to spam(eggs(a, b), c)
>
>         arg->eggs->spam(b,c)    # equivalent to spam(eggs(arg, b), c)
>
>
>     This would become:
>
>     arg->eggs(b)->spam(c)     # equivalent to spam(eggs(arg, b), c)
>
>     Note that this would be quite flexible in partial 'piping' of
>     multi-argument functions.
>
>         So you could think of -> as an extended partial operator. And
>         this would naturally generalize to functions with even more
>         arguments. The arguments would always be fed in the same order
>         as in the equivalent function call, which makes for a nice
>         rule of thumb. However, I suppose one would usually avoid
>         combinations that are difficult to understand.
>
>         Some examples that this would enable:
>
>          # Example 1
>          from numpy import square, mean, sqrt
>          rms = square->mean->sqrt  # I think this order is fine
>         because it is not @
>
>
>     This would become:
>
>     def rms(arr):
>         return arr->square()->mean()->sqrt()
>
>          # Example 2 (both are equivalent)
>          spam(args)->eggs->cheese() # the shell-syntax analogy that
>         Steven mentioned.
>
>
>     This would be:
>
>     spam(args)->eggs()->cheese()
>
>     Of course the shell piping analogy would be quite far, because it
>     looks so different.
>
>          # Example 3
>          # Last but not least, we would finally have this :)
>          some_sequence->len()
>          some_object->isinstance(MyType)
>
>
>     And:
>
>      func->map(seq)
>      func->reduce(seq)
>
>     -- Koos
>
>
>
>
>
>     _______________________________________________
>     Python-ideas mailing list
>     Python-ideas at python.org <mailto:Python-ideas at python.org>
>     https://mail.python.org/mailman/listinfo/python-ideas
>     Code of Conduct: http://python.org/psf/codeofconduct/
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20150511/c22f2ac5/attachment-0001.html>


More information about the Python-ideas mailing list