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

Gregory Salvan apieum at gmail.com
Mon May 11 01:40:23 CEST 2015


Nope sorry I've misread your code, but it changes nothing.

for example with spam(args)->eggs()->cheese()

if instead you have:
args=something
spam = lambda: args

spam()->eggs()->cheese()
should be treaten as: cheese(eggs(spam())) or cheese(eggs(args)) or
partial(cheese) circle partial(eggs) circle partial(spam) ?


I don't find this syntax convenient, sorry.




2015-05-10 23:41 GMT+02:00 Koos Zevenhoven <koos.zevenhoven at aalto.fi>:

>  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>:
>
>> 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
>> 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/507d72dd/attachment-0001.html>


More information about the Python-ideas mailing list