"Immutable Builder" Pattern and Operator

[snip]
I propose `x .= y` -> `x = x . y`, for any `y`.
[snip] I think you mean "any y that is a member of x" Also, note that this syntax means that x will be rebound to the result of calling x.y, whatever that is (frequently, None, for mutating methods) In general, you can't count on methods to return references to their instances, even though it's handy for fluent coding, so this side effect may be unexpected to some That's a problem with your original example:
long_name = mkbuilder()
long_name = long_name.seta(a)
long_name = long_name.setb(b)
y = long_name.build()
What do the methods seta and setb return? If they don't return "self" you've got a problem. I think. FWIW why can't you just write: x.y or for your example: long_name.seta(a) ?

On 23/01/17 01:52 PM, Gerald Britton wrote:
Since it desugars into `x = x.y`, you can literally use anything for `y`. x .= __call__().whatever().unwrap() * 3 is equivalent to x = x.__call__().whatever().unwrap() * 3 and x .= 1 is equivalent to x = x.1 which is equivalent to SyntaxError: invalid syntax
This is why it's for use with **immutable** objects.
They don't return self. Ever. The value bound to long_name is immutable, like an integer. They return a new instance.
See the IRC bot builder example, it should be more clear. (It's about forking the builder.)

On Mon, Jan 23, 2017 at 8:07 AM, Soni L. <fakedme+py@gmail.com> wrote:
And that's exactly the problem. Users would be greatly confused because what's to the right of `.=` is *not* an expression, it's something more restricted (in particular it must start with a plain identifier). This makes the `.=` operator a very different beast from `=`, `+=` and friends. I assume you think that's fine, but given your cavalier attitude about `x .= 1` my feeling is that you don't have a lot of experience designing and implementing language features. That is okay, you are learning it here. But perhaps you should take the hint from the large number of people here who have gently tried to explain to you that while this is a good idea, it's not a great idea, and there's no sufficiently important use case to make up for the confusion (indicated above) that it will inevitably cause. -- --Guido van Rossum (python.org/~guido)

On Jan 23, 2017 11:07 AM, "Soni L." <fakedme+py@gmail.com> wrote: On 23/01/17 01:52 PM, Gerald Britton wrote: [snip]
I propose `x .= y` -> `x = x . y`, for any `y`.
[snip] I think you mean "any y that is a member of x" Since it desugars into `x = x.y`, you can literally use anything for `y`. x .= __call__().whatever().unwrap() * 3 is equivalent to x = x.__call__().whatever().unwrap() * 3 and x .= 1 is equivalent to x = x.1 which is equivalent to SyntaxError: invalid syntax Also, note that this syntax means that x will be rebound to the result of calling x.y, whatever that is (frequently, None, for mutating methods) In general, you can't count on methods to return references to their instances, even though it's handy for fluent coding, so this side effect may be unexpected to some This is why it's for use with **immutable** objects. That's a problem with your original example:
long_name = mkbuilder()
long_name = long_name.seta(a)
long_name = long_name.setb(b)
y = long_name.build()
What do the methods seta and setb return? If they don't return "self" you've got a problem. I think. They don't return self. Ever. The value bound to long_name is immutable, like an integer. They return a new instance. Then long_name isn't immutable. It changes with every line. That can lead to nasty bugs if you count on its immutability. Easy to see. Just print long_name after each call. FWIW why can't you just write: x.y or for your example: long_name.seta(a) ? See the IRC bot builder example, it should be more clear. (It's about forking the builder.)

On 23/01/17 02:56 PM, Gerald Britton wrote:
You're mixing up value immutability with name immutability. The name isn't immutable, but: long_name = mkbuilder() x = long_name long_name .= seta("a") y = long_name long_name .= setb("b") z = long_name print(x) # a = None, b = None print(y) # a = "a", b = None print(z) # a = "a", b = "b" print(x is y) # False print(x is z) # False print(y is z) # False print(long_name is z) # True See also: long_name = 1 x = long_name long_name += 1 y = long_name long_name += 1 z = long_name print(x) # 1 print(y) # 2 print(z) # 3 print(x is y) # False print(x is z) # False print(y is z) # False print(long_name is z) # True

On 2017-01-23 12:54 PM, Soni L. wrote:
All your prints work as well, this is much easier to mentally parse (notwithstanding the unpythonesque builder pattern) and doesn't require the addition of a new syntax that people more knowledgeable than me have already explained is not a good idea --Alex

On 23/01/17 01:52 PM, Gerald Britton wrote:
Since it desugars into `x = x.y`, you can literally use anything for `y`. x .= __call__().whatever().unwrap() * 3 is equivalent to x = x.__call__().whatever().unwrap() * 3 and x .= 1 is equivalent to x = x.1 which is equivalent to SyntaxError: invalid syntax
This is why it's for use with **immutable** objects.
They don't return self. Ever. The value bound to long_name is immutable, like an integer. They return a new instance.
See the IRC bot builder example, it should be more clear. (It's about forking the builder.)

On Mon, Jan 23, 2017 at 8:07 AM, Soni L. <fakedme+py@gmail.com> wrote:
And that's exactly the problem. Users would be greatly confused because what's to the right of `.=` is *not* an expression, it's something more restricted (in particular it must start with a plain identifier). This makes the `.=` operator a very different beast from `=`, `+=` and friends. I assume you think that's fine, but given your cavalier attitude about `x .= 1` my feeling is that you don't have a lot of experience designing and implementing language features. That is okay, you are learning it here. But perhaps you should take the hint from the large number of people here who have gently tried to explain to you that while this is a good idea, it's not a great idea, and there's no sufficiently important use case to make up for the confusion (indicated above) that it will inevitably cause. -- --Guido van Rossum (python.org/~guido)

On Jan 23, 2017 11:07 AM, "Soni L." <fakedme+py@gmail.com> wrote: On 23/01/17 01:52 PM, Gerald Britton wrote: [snip]
I propose `x .= y` -> `x = x . y`, for any `y`.
[snip] I think you mean "any y that is a member of x" Since it desugars into `x = x.y`, you can literally use anything for `y`. x .= __call__().whatever().unwrap() * 3 is equivalent to x = x.__call__().whatever().unwrap() * 3 and x .= 1 is equivalent to x = x.1 which is equivalent to SyntaxError: invalid syntax Also, note that this syntax means that x will be rebound to the result of calling x.y, whatever that is (frequently, None, for mutating methods) In general, you can't count on methods to return references to their instances, even though it's handy for fluent coding, so this side effect may be unexpected to some This is why it's for use with **immutable** objects. That's a problem with your original example:
long_name = mkbuilder()
long_name = long_name.seta(a)
long_name = long_name.setb(b)
y = long_name.build()
What do the methods seta and setb return? If they don't return "self" you've got a problem. I think. They don't return self. Ever. The value bound to long_name is immutable, like an integer. They return a new instance. Then long_name isn't immutable. It changes with every line. That can lead to nasty bugs if you count on its immutability. Easy to see. Just print long_name after each call. FWIW why can't you just write: x.y or for your example: long_name.seta(a) ? See the IRC bot builder example, it should be more clear. (It's about forking the builder.)

On 23/01/17 02:56 PM, Gerald Britton wrote:
You're mixing up value immutability with name immutability. The name isn't immutable, but: long_name = mkbuilder() x = long_name long_name .= seta("a") y = long_name long_name .= setb("b") z = long_name print(x) # a = None, b = None print(y) # a = "a", b = None print(z) # a = "a", b = "b" print(x is y) # False print(x is z) # False print(y is z) # False print(long_name is z) # True See also: long_name = 1 x = long_name long_name += 1 y = long_name long_name += 1 z = long_name print(x) # 1 print(y) # 2 print(z) # 3 print(x is y) # False print(x is z) # False print(y is z) # False print(long_name is z) # True

On 2017-01-23 12:54 PM, Soni L. wrote:
All your prints work as well, this is much easier to mentally parse (notwithstanding the unpythonesque builder pattern) and doesn't require the addition of a new syntax that people more knowledgeable than me have already explained is not a good idea --Alex
participants (4)
-
Alexandre Brault
-
Gerald Britton
-
Guido van Rossum
-
Soni L.