Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-12 Thread Christopher Barker
On Fri, Mar 8, 2019 at 1:52 AM Jonathan Fine  wrote:

> I've just learnt something new. Look at
>
> >>> from operator import iadd
> >>> lst = [1, 2, 3]
> >>> iadd(lst, 'hi')
> [1, 2, 3, 'h', 'i']
> >>> lst
> [1, 2, 3, 'h', 'i']
>
> This shows that the proposals dict.flow_update and dict.__iadd__ are
> basically the same. (I think this is quite important for understanding
> the attraction of fluent programming. We ALREADY like and use it, in
> the form of augmented assignment of mutables.)
>

well, no -- the fact that __iadd__ acts like it does is essentially an
accident of implementation, and calling __iadd__ directly is frowned upon
(and I'm not totally sure if it is guaranteed to keep working that way by
the language spec). And, in fact, it DOESN'T act like flow_merge method --
as it both mutates the original object, and returns itself -- which I think
is a no-no in fluent programming, yes? (certainly in functional programming)

In [10]: list1 = [1,2,3]

In [11]: list2 = [4,5,6]

In [12]: list3 = list1.__iadd__(list2)

In [13]: list3
Out[13]: [1, 2, 3, 4, 5, 6]

In [14]: list1
Out[14]: [1, 2, 3, 4, 5, 6]

In [15]: list1 is list3
Out[15]: True


This also shows that
>combined = defaults.copy()
>combined.update(options)
> could, if the proposal is accepted, be written as
>defaults.copy().__iadd__(options)
>

did you mean:

combined = defaults.copy().__iadd__(options)

because the way you wrote it, you are making a copy, mutating it, and then
throwing it away...

in which case, yes it could, but it would not be recommended, and I can't
see the advantage of it over:

combined = defaults + options

or even, if you REALLY want to use __ methods:

combined = defaults.__add__(options)


In [17]: list3 = list1.__add__(list2)

In [18]: list1
Out[18]: [1, 2, 3]

In [19]: list3
Out[19]: [1, 2, 3, 4, 5, 6]


> I got the idea from the withdrawn PEP (thank you, Nick Coghlan, for
> writing it):
> PEP 577 -- Augmented Assignment Expressions
> https://www.python.org/dev/peps/pep-0577/
>

Interestingly (to me) it was withdrawn for different reasons than what I
would think -- mutating and assigning at once is dangerous.

-CHB


-- 
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-08 Thread Jonathan Fine
I've just learnt something new. Look at

>>> from operator import iadd
>>> lst = [1, 2, 3]
>>> iadd(lst, 'hi')
[1, 2, 3, 'h', 'i']
>>> lst
[1, 2, 3, 'h', 'i']

This shows that the proposals dict.flow_update and dict.__iadd__ are
basically the same. (I think this is quite important for understanding
the attraction of fluent programming. We ALREADY like and use it, in
the form of augmented assignment of mutables.)

This also shows that
   combined = defaults.copy()
   combined.update(options)
could, if the proposal is accepted, be written as
   defaults.copy().__iadd__(options)

I got the idea from the withdrawn PEP (thank you, Nick Coghlan, for writing it):
PEP 577 -- Augmented Assignment Expressions
https://www.python.org/dev/peps/pep-0577/

-- 
Jonathan
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-06 Thread Christopher Barker
Do go read the recent thread about this - there is a lot there!

Titled something like “fluent programming”

Sorry — on a phone, kinda hard to check now.

-CHB

-- 
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-06 Thread Brice Parent
Why not simply propose an external lib with FluentDict and other 
Fluent[Anything] already packaged? I don't know if I'd use it 
personnally, but it definitely could have some users.



Le 05/03/2019 à 09:48, Jonathan Fine a écrit :

SUMMARY
Instead of using dict + dict, perhaps use dict.flow_update. Here,
flow_update is just like update, except that it returns self.

BACKGROUND
There's a difference between a sorted copy of a list, and sorting the
list in place.

 >>> items = [2, 0, 1, 9]
 >>> sorted(items), items
 ([0, 1, 2, 9], [2, 0, 1, 9])
 >>> items.sort(), items
(None, [0, 1, 2, 9])

In Python, mutating methods generally return None. Here, this prevents
beginners thinking their code has produced a sorted copy of a list,
when in fact it has done an in-place sort on the list. If they write
 >>> aaa = my_list.sort()
they'll get a None error when they use aaa.

The same goes for dict.update. This is a useful feature, particularly
for beginners. It helps them think clearly, and express themselves
clearly.

THE PROBLEM
This returning None can be a nuisance, sometimes. Suppose we have a
dictionary of default values, and a dictionary of use supplied
options. We wish to combine the two dictionaries, say into a new
combined dictionary.

One way to do this is:

combined = defaults.copy()
combined.update(options)

But this is awkward when you're in the middle of calling a function:

   call_big_method(
   # lots of arguments, one to a line, with comments
   arg = combined, # Look up to see what combined is.
  # more arguments
 )

USING +
There's a suggestion, that instead one extends Python so that this works:
 arg = defaults + options # What does '+' mean here?

USING flow_update
Here's another suggestion. Instead write:
 dict_arg = defaults.copy().flow_update(options) # Is this clearer?

IMPLEMENTATION
Here's an implementation, as a subclass of dict.

 class mydict(dict):

 def flow_update(self, *argv, **kwargs):
 self.update(*argv, **kwargs)
 return self

 def copy(self):
 return self.__class__(self)

A DIRTY HACK
Not tested, using an assignment expression.
dict_arg = (tmp := defaults.copy(), tmp.update(options))[0]
Not recommend.



___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Wes Turner
dicttoolz has functions for working with these objects; including
dicttoolz.merge (which returns a reference to the merged dicts but does not
mutate the arguments passed).

https://toolz.readthedocs.io/en/latest/api.html#dicttoolz

https://toolz.readthedocs.io/en/latest/api.html#toolz.dicttoolz.merge

pyrsistent has a PRecord class with invariants and type checking that
precedes dataclasses. pyrsistent also has 'freeze' and 'thaw' functions for
immutability. PRecord extends PMap, which implements __add__ as
self.update(arg) (which does not mutate self)
https://github.com/tobgu/pyrsistent/blob/master/README.rst#precord

https://github.com/tobgu/pyrsistent/blob/master/pyrsistent/_pmap.py

On Tuesday, March 5, 2019, Guido van Rossum  wrote:

> If you have to tell such a long and convoluted story to explain a name
> that you've picked out of the blue and that has no equivalent in other
> Python data types, it's probably a bad idea. If you're proposing that other
> mutating methods also gain a flow_XXX variant, please, no! That's like the
> theory of supersymmetry (SUSY) in particle physics, where ever known
> particle from the Standard Model would have to have a much heavier
> "superpartner" just to make some esoteric idea work.
>
> On Tue, Mar 5, 2019 at 12:54 AM Jonathan Fine  wrote:
>
>> SUMMARY
>> Instead of using dict + dict, perhaps use dict.flow_update. Here,
>> flow_update is just like update, except that it returns self.
>>
>> BACKGROUND
>> There's a difference between a sorted copy of a list, and sorting the
>> list in place.
>>
>> >>> items = [2, 0, 1, 9]
>> >>> sorted(items), items
>> ([0, 1, 2, 9], [2, 0, 1, 9])
>> >>> items.sort(), items
>>(None, [0, 1, 2, 9])
>>
>> In Python, mutating methods generally return None. Here, this prevents
>> beginners thinking their code has produced a sorted copy of a list,
>> when in fact it has done an in-place sort on the list. If they write
>> >>> aaa = my_list.sort()
>> they'll get a None error when they use aaa.
>>
>> The same goes for dict.update. This is a useful feature, particularly
>> for beginners. It helps them think clearly, and express themselves
>> clearly.
>>
>> THE PROBLEM
>> This returning None can be a nuisance, sometimes. Suppose we have a
>> dictionary of default values, and a dictionary of use supplied
>> options. We wish to combine the two dictionaries, say into a new
>> combined dictionary.
>>
>> One way to do this is:
>>
>>combined = defaults.copy()
>>combined.update(options)
>>
>> But this is awkward when you're in the middle of calling a function:
>>
>>   call_big_method(
>>   # lots of arguments, one to a line, with comments
>>   arg = combined, # Look up to see what combined is.
>>  # more arguments
>> )
>>
>> USING +
>> There's a suggestion, that instead one extends Python so that this works:
>> arg = defaults + options # What does '+' mean here?
>>
>> USING flow_update
>> Here's another suggestion. Instead write:
>> dict_arg = defaults.copy().flow_update(options) # Is this
>> clearer?
>>
>> IMPLEMENTATION
>> Here's an implementation, as a subclass of dict.
>>
>> class mydict(dict):
>>
>> def flow_update(self, *argv, **kwargs):
>> self.update(*argv, **kwargs)
>> return self
>>
>> def copy(self):
>> return self.__class__(self)
>>
>> A DIRTY HACK
>> Not tested, using an assignment expression.
>>dict_arg = (tmp := defaults.copy(), tmp.update(options))[0]
>> Not recommend.
>>
>> --
>> Jonathan
>> ___
>> Python-ideas mailing list
>> Python-ideas@python.org
>> https://mail.python.org/mailman/listinfo/python-ideas
>> Code of Conduct: http://python.org/psf/codeofconduct/
>>
>
>
> --
> --Guido van Rossum (python.org/~guido)
>
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Greg Ewing

Christopher Barker wrote:
That violates an important convention in Python: mutating methods do not 
return self. We really want to preserve that convention.


Smalltalk has an abbreviated way of writing a series of method
calls to the same object:

   x doThis; doThatWith: y; doTheOther.

is equivalent to

   x doThis.
   x doThatWith: y.
   x doTheOther.

Something like this could no doubt be added to Python, but I'm
not sure it would be worth the bother. Giving a short name to the
recipient and then writing the calls out explicitly isn't much
harder and is clearer to read, IMO.

--
Greg
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Jonathan Fine
I thank Guido and Christopher for their thoughtful comments. You
certainly found some weak points. I chose the name 'flow' to match:
https://en.wikipedia.org/wiki/Fluent_interface#Python

Instead of my previous

   arg = defaults.copy().flow_update(options)

one could instead from somewhere import flow, and then write

   arg = flow(defaults.copy()).update(options)

This avoids a profusion of flow_ methods, and also the need to
subclass dict. Once could of course use a different name. Perhaps
'follow' would be better. And it would work 'out the box' in other
situations.

Christopher might prefer the flow(obj).update approach, as it respects
the convention "mutating methods do not return self." (Thank you for
your clear statement, Christopher.)

(Aside: For the non-experts, a word if I may about the implementation.
The key point is that in Python the programmer 'owns the dot' and so
the desired semantics can be implemented. We use a custom
__getattribute__ .)

Finally, please forgive my fictional use case. I think studying
real-world use cases for dict + dict would be very helpful to the
discussion. I don't recall seeing any, but I haven't looked hard.
Instructive use cases should, of course, be placed in the PEP.

-- 
Jonathan
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Christopher Barker
On Tue, Mar 5, 2019 at 12:53 AM Jonathan Fine  wrote:

> SUMMARY
> Instead of using dict + dict, perhaps use dict.flow_update. Here,
> flow_update is just like update, except that it returns self.


That violates an important convention in Python: mutating methods do not
return self. We really want to preserve that convention.

On the other hand, as seen in other recent threads, there is a desire for
chaining operations of many sorts, so a .flow_update() that returned a new
dict would provide that feature.

Though I would only recommend that if it was decided that we wanted to
generally support that approach for all mutable containers — which would
mean adding quite a few methods.

And we could then use the same naming convention for them all.

I’m not sure I like “flow_” though, it’s not very commonly known jargon.

-CHB

-- 
Christopher Barker, PhD

Python Language Consulting
  - Teaching
  - Scientific Software Development
  - Desktop GUI and Web Development
  - wxPython, numpy, scipy, Cython
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Guido van Rossum
If you have to tell such a long and convoluted story to explain a name that
you've picked out of the blue and that has no equivalent in other Python
data types, it's probably a bad idea. If you're proposing that other
mutating methods also gain a flow_XXX variant, please, no! That's like the
theory of supersymmetry (SUSY) in particle physics, where ever known
particle from the Standard Model would have to have a much heavier
"superpartner" just to make some esoteric idea work.

On Tue, Mar 5, 2019 at 12:54 AM Jonathan Fine  wrote:

> SUMMARY
> Instead of using dict + dict, perhaps use dict.flow_update. Here,
> flow_update is just like update, except that it returns self.
>
> BACKGROUND
> There's a difference between a sorted copy of a list, and sorting the
> list in place.
>
> >>> items = [2, 0, 1, 9]
> >>> sorted(items), items
> ([0, 1, 2, 9], [2, 0, 1, 9])
> >>> items.sort(), items
>(None, [0, 1, 2, 9])
>
> In Python, mutating methods generally return None. Here, this prevents
> beginners thinking their code has produced a sorted copy of a list,
> when in fact it has done an in-place sort on the list. If they write
> >>> aaa = my_list.sort()
> they'll get a None error when they use aaa.
>
> The same goes for dict.update. This is a useful feature, particularly
> for beginners. It helps them think clearly, and express themselves
> clearly.
>
> THE PROBLEM
> This returning None can be a nuisance, sometimes. Suppose we have a
> dictionary of default values, and a dictionary of use supplied
> options. We wish to combine the two dictionaries, say into a new
> combined dictionary.
>
> One way to do this is:
>
>combined = defaults.copy()
>combined.update(options)
>
> But this is awkward when you're in the middle of calling a function:
>
>   call_big_method(
>   # lots of arguments, one to a line, with comments
>   arg = combined, # Look up to see what combined is.
>  # more arguments
> )
>
> USING +
> There's a suggestion, that instead one extends Python so that this works:
> arg = defaults + options # What does '+' mean here?
>
> USING flow_update
> Here's another suggestion. Instead write:
> dict_arg = defaults.copy().flow_update(options) # Is this clearer?
>
> IMPLEMENTATION
> Here's an implementation, as a subclass of dict.
>
> class mydict(dict):
>
> def flow_update(self, *argv, **kwargs):
> self.update(*argv, **kwargs)
> return self
>
> def copy(self):
> return self.__class__(self)
>
> A DIRTY HACK
> Not tested, using an assignment expression.
>dict_arg = (tmp := defaults.copy(), tmp.update(options))[0]
> Not recommend.
>
> --
> Jonathan
> ___
> Python-ideas mailing list
> Python-ideas@python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
--Guido van Rossum (python.org/~guido)
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Suggestions: dict.flow_update and dict.__add__

2019-03-05 Thread Jonathan Fine
SUMMARY
Instead of using dict + dict, perhaps use dict.flow_update. Here,
flow_update is just like update, except that it returns self.

BACKGROUND
There's a difference between a sorted copy of a list, and sorting the
list in place.

>>> items = [2, 0, 1, 9]
>>> sorted(items), items
([0, 1, 2, 9], [2, 0, 1, 9])
>>> items.sort(), items
   (None, [0, 1, 2, 9])

In Python, mutating methods generally return None. Here, this prevents
beginners thinking their code has produced a sorted copy of a list,
when in fact it has done an in-place sort on the list. If they write
>>> aaa = my_list.sort()
they'll get a None error when they use aaa.

The same goes for dict.update. This is a useful feature, particularly
for beginners. It helps them think clearly, and express themselves
clearly.

THE PROBLEM
This returning None can be a nuisance, sometimes. Suppose we have a
dictionary of default values, and a dictionary of use supplied
options. We wish to combine the two dictionaries, say into a new
combined dictionary.

One way to do this is:

   combined = defaults.copy()
   combined.update(options)

But this is awkward when you're in the middle of calling a function:

  call_big_method(
  # lots of arguments, one to a line, with comments
  arg = combined, # Look up to see what combined is.
 # more arguments
)

USING +
There's a suggestion, that instead one extends Python so that this works:
arg = defaults + options # What does '+' mean here?

USING flow_update
Here's another suggestion. Instead write:
dict_arg = defaults.copy().flow_update(options) # Is this clearer?

IMPLEMENTATION
Here's an implementation, as a subclass of dict.

class mydict(dict):

def flow_update(self, *argv, **kwargs):
self.update(*argv, **kwargs)
return self

def copy(self):
return self.__class__(self)

A DIRTY HACK
Not tested, using an assignment expression.
   dict_arg = (tmp := defaults.copy(), tmp.update(options))[0]
Not recommend.

-- 
Jonathan
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/