"Immutable Builder" Pattern and Operator
![](https://secure.gravatar.com/avatar/3a304881dc609b0382ec45d20b7ae9c5.jpg?s=120&d=mm&r=g)
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work: long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build() Instead, you'd need something more like this: long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build() Or we could add an operator to simplify it: long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build() (Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.)
![](https://secure.gravatar.com/avatar/4c01705256aa2160c1354790e8c154db.jpg?s=120&d=mm&r=g)
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is: y = build(a=a, b=b) A Builder pattern is less used in Python due to the support of keyword arguments.
![](https://secure.gravatar.com/avatar/2240a37aad5f5834a92809a5e5f01fe1.jpg?s=120&d=mm&r=g)
This is easy to do in Python, and we already have NamedTuples and other things. If you need such a builder anyway, this snippet can work - no need for special syntax: https://gist.github.com/jsbueno/b2b5f5c06caa915c451253bb4f171ee9 On 22 January 2017 at 20:54, Serhiy Storchaka <storchaka@gmail.com> wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
![](https://secure.gravatar.com/avatar/3a304881dc609b0382ec45d20b7ae9c5.jpg?s=120&d=mm&r=g)
On 22/01/17 08:54 PM, Serhiy Storchaka wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
I guess you could do something like this, for an IRC bot builder: fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC But this would be cleaner: botbuilder = mkircbotbuilder().network("irc.freenode.net").port(6697).ssl(true) mainbot = botbuilder.channels(["#bots"]).build() botbuilder .= dcc(true).channeldcc(true) dccbot = botbuilder.channels(["#ctcp-s"]).build() botbuilder .= network("irc.subluminal.net") otherbot = botbuilder.channels(["#programming"]).build() (I mean, "channels" could/should be a per-bot property instead of a per-builder property... but that doesn't affect the examples much.)
![](https://secure.gravatar.com/avatar/de311342220232e618cb27c9936ab9bf.jpg?s=120&d=mm&r=g)
On 01/22/2017 03:30 PM, Soni L. wrote:
On 22/01/17 08:54 PM, Serhiy Storchaka wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
I guess you could do something like this, for an IRC bot builder:
fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC
The following is just fine: fnircbotbuilder = mkircbotbuilder( network="irc.freenode.net", port=6697, ssl=true, ) mainbot = fnircbotbuilder(channels=["#bots"]).build() fndccbotbuilder = fnircbotbuilder(dcc=true, channeldcc=true) dccbot = fndccbotbuilder(channels=["#ctcp-s"]).build() otherircbotbuilder = fndccbotbuilder(network="irc.subluminal.net") otherbot = otherircbotbuilder(channels=["#programming"]).build() -- ~Ethan~
![](https://secure.gravatar.com/avatar/4c01705256aa2160c1354790e8c154db.jpg?s=120&d=mm&r=g)
On 23.01.17 01:30, Soni L. wrote:
On 22/01/17 08:54 PM, Serhiy Storchaka wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
I guess you could do something like this, for an IRC bot builder:
fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC
But this would be cleaner:
botbuilder = mkircbotbuilder().network("irc.freenode.net").port(6697).ssl(true) mainbot = botbuilder.channels(["#bots"]).build() botbuilder .= dcc(true).channeldcc(true) dccbot = botbuilder.channels(["#ctcp-s"]).build() botbuilder .= network("irc.subluminal.net") otherbot = botbuilder.channels(["#programming"]).build()
In Python you can save common options in a dict and pass them as var-keyword argument. Or use functools.partial. In any case you don't need a builder class with the build method and a number of configuring methods. It can be just a function with optional keyword parameters. A Builder pattern is often used in languages that don't support passing arguments by keyword and partial functions. Python rarely needs the purposed class implementing a Builder pattern. Actually a Builder pattern is built-in in the language as a part of syntax.
![](https://secure.gravatar.com/avatar/3a304881dc609b0382ec45d20b7ae9c5.jpg?s=120&d=mm&r=g)
On 23/01/17 09:45 AM, Serhiy Storchaka wrote:
On 23.01.17 01:30, Soni L. wrote:
On 22/01/17 08:54 PM, Serhiy Storchaka wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
I guess you could do something like this, for an IRC bot builder:
fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC
But this would be cleaner:
botbuilder = mkircbotbuilder().network("irc.freenode.net").port(6697).ssl(true) mainbot = botbuilder.channels(["#bots"]).build() botbuilder .= dcc(true).channeldcc(true) dccbot = botbuilder.channels(["#ctcp-s"]).build() botbuilder .= network("irc.subluminal.net") otherbot = botbuilder.channels(["#programming"]).build()
In Python you can save common options in a dict and pass them as var-keyword argument. Or use functools.partial. In any case you don't need a builder class with the build method and a number of configuring methods. It can be just a function with optional keyword parameters.
A Builder pattern is often used in languages that don't support passing arguments by keyword and partial functions. Python rarely needs the purposed class implementing a Builder pattern. Actually a Builder pattern is built-in in the language as a part of syntax.
Yeah but the dotequals operator has many other benefits: long_name .= __call__ # cast to callable long_name .= wrapped # unwrap etc And it also looks neat.
![](https://secure.gravatar.com/avatar/0a2191a85455df6d2efdb22c7463c304.jpg?s=120&d=mm&r=g)
On 23.01.2017 14:05, Soni L. wrote:
On 23/01/17 09:45 AM, Serhiy Storchaka wrote:
On 23.01.17 01:30, Soni L. wrote:
On 22/01/17 08:54 PM, Serhiy Storchaka wrote:
On 23.01.17 00:45, Soni L. wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
I think the more pythonic way is:
y = build(a=a, b=b)
A Builder pattern is less used in Python due to the support of keyword arguments.
I guess you could do something like this, for an IRC bot builder:
fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC
But this would be cleaner:
botbuilder = mkircbotbuilder().network("irc.freenode.net").port(6697).ssl(true) mainbot = botbuilder.channels(["#bots"]).build() botbuilder .= dcc(true).channeldcc(true) dccbot = botbuilder.channels(["#ctcp-s"]).build() botbuilder .= network("irc.subluminal.net") otherbot = botbuilder.channels(["#programming"]).build()
In Python you can save common options in a dict and pass them as var-keyword argument. Or use functools.partial. In any case you don't need a builder class with the build method and a number of configuring methods. It can be just a function with optional keyword parameters.
A Builder pattern is often used in languages that don't support passing arguments by keyword and partial functions. Python rarely needs the purposed class implementing a Builder pattern. Actually a Builder pattern is built-in in the language as a part of syntax.
Yeah but the dotequals operator has many other benefits:
long_name .= __call__ # cast to callable long_name .= wrapped # unwrap etc
And it also looks neat.
I don't see this an being a particular intuitive way of writing such rather uncommon constructs. The syntax is not clear (what if you have an expression on the RHS) and it doesn't save you much in writing (if long_name is too long simply rebind it under a shorter name for the purpose of the code block). Also note that rebinding different objects to the same name in the same block is often poor style and can easily lead to hard to track bugs. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, Jan 23 2017)
Python Projects, Coaching and Consulting ... http://www.egenix.com/ Python Database Interfaces ... http://products.egenix.com/ Plone/Zope Database Interfaces ... http://zope.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ http://www.malemburg.com/
![](https://secure.gravatar.com/avatar/d995b462a98fea412efa79d17ba3787a.jpg?s=120&d=mm&r=g)
On 23 January 2017 at 13:05, Soni L. <fakedme+py@gmail.com> wrote:
Yeah but the dotequals operator has many other benefits:
long_name .= __call__ # cast to callable long_name .= wrapped # unwrap etc
Those don't seem particularly clear to me.
And it also looks neat.
Well, we have to agree to differ on that one. Also, the semantics of the proposed operation are very odd. If I understand your proposal a .= b(c) doesn't evaluate b(c) (It can't, as b is a method of a and doesn't make sense on its own), but rather combines the LHS and RHS with a dot - so it's defined in terms of rewriting the input rather than as an operation on the subexpressions. There's no other operator in Python that I'm aware of that works like this. What grammar would you allow for the RHS? So far you've shown LHS .= METHOD(ARGS) LHS .= ATTRIBUTE Clearly, LHS .= EXPR makes no sense in general (consider a .= 1+1). On the other hand, what about LHS .= ATTRIBUTE[INDEX] ? I'm guessing you'd want that allowed? Frankly, I don't think the benefits are even close to justifying the complexity. Paul
![](https://secure.gravatar.com/avatar/63ca18e130d527d0741f1da54bb129a7.jpg?s=120&d=mm&r=g)
Have you looked at pyrsistent for immutable/functional/persistent/copy-on-write data structures in Python? https://github.com/tobgu/pyrsistent/ (freeze() / thaw()) ... e.g. List and Dict NamedTuple values are not immutable (because append() and update() still work) On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
![](https://secure.gravatar.com/avatar/63ca18e130d527d0741f1da54bb129a7.jpg?s=120&d=mm&r=g)
On Sunday, January 22, 2017, Wes Turner <wes.turner@gmail.com> wrote:
Have you looked at pyrsistent for immutable/functional/persistent/copy-on-write data structures in Python?
https://github.com/tobgu/pyrsistent/
(freeze() / thaw())
... e.g. List and Dict NamedTuple values are not immutable (because append() and update() still work)
fn.py also has immutables: https://github.com/kachayev/fn.py/blob/master/README.rst#persistent-data-str...
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com <javascript:_e(%7B%7D,'cvml','fakedme%2Bpy@gmail.com');>> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
![](https://secure.gravatar.com/avatar/3a304881dc609b0382ec45d20b7ae9c5.jpg?s=120&d=mm&r=g)
On 22/01/17 10:03 PM, Wes Turner wrote:
On Sunday, January 22, 2017, Wes Turner <wes.turner@gmail.com <mailto:wes.turner@gmail.com>> wrote:
Have you looked at pyrsistent for immutable/functional/persistent/copy-on-write data structures in Python?
https://github.com/tobgu/pyrsistent/ <https://github.com/tobgu/pyrsistent/>
(freeze() / thaw())
... e.g. List and Dict NamedTuple values are not immutable (because append() and update() still work)
fn.py also has immutables: https://github.com/kachayev/fn.py/blob/master/README.rst#persistent-data-str...
You seem to be thinking of "immutable object builder". Not "the builder itself is immutable and operations on it create new builders".
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com <javascript:_e(%7B%7D,'cvml','fakedme%2Bpy@gmail.com');>> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas <https://mail.python.org/mailman/listinfo/python-ideas> Code of Conduct: http://python.org/psf/codeofconduct/ <http://python.org/psf/codeofconduct/>
![](https://secure.gravatar.com/avatar/63ca18e130d527d0741f1da54bb129a7.jpg?s=120&d=mm&r=g)
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com> wrote:
On 22/01/17 10:03 PM, Wes Turner wrote:
On Sunday, January 22, 2017, Wes Turner <wes.turner@gmail.com <javascript:_e(%7B%7D,'cvml','wes.turner@gmail.com');>> wrote:
Have you looked at pyrsistent for immutable/functional/persistent/copy-on-write data structures in Python?
https://github.com/tobgu/pyrsistent/
(freeze() / thaw())
... e.g. List and Dict NamedTuple values are not immutable (because append() and update() still work)
fn.py also has immutables: https://github.com/kachayev/fn.py/blob/master/README.rst# persistent-data-structures
You seem to be thinking of "immutable object builder". Not "the builder itself is immutable and operations on it create new builders".
My mistake. Something like @optionable and/or @curried from fn.py in conjunction with PClass from pyrsistent may accomplish what you describe?
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
![](https://secure.gravatar.com/avatar/63ca18e130d527d0741f1da54bb129a7.jpg?s=120&d=mm&r=g)
On Sunday, January 22, 2017, Wes Turner <wes.turner@gmail.com> wrote:
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com <javascript:_e(%7B%7D,'cvml','fakedme%2Bpy@gmail.com');>> wrote:
On 22/01/17 10:03 PM, Wes Turner wrote:
On Sunday, January 22, 2017, Wes Turner <wes.turner@gmail.com> wrote:
Have you looked at pyrsistent for immutable/functional/persistent/copy-on-write data structures in Python?
https://github.com/tobgu/pyrsistent/
(freeze() / thaw())
... e.g. List and Dict NamedTuple values are not immutable (because append() and update() still work)
fn.py also has immutables: https://github.com/kachayev/fn.py/blob/master/README.rst#per sistent-data-structures
You seem to be thinking of "immutable object builder". Not "the builder itself is immutable and operations on it create new builders".
My mistake. Something like @optionable and/or @curried from fn.py in conjunction with PClass from pyrsistent may accomplish what you describe?
From http://pyrsistent.readthedocs.io/en/latest/api.html#pyrsistent.PClass.set : "Set a field in the instance. Returns a new instance with the updated value. The original instance remains unmodified. Accepts key-value pairs or single string representing the field name and a value."
On Sunday, January 22, 2017, Soni L. <fakedme+py@gmail.com> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.) _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
![](https://secure.gravatar.com/avatar/5615a372d9866f203a22b2c437527bbb.jpg?s=120&d=mm&r=g)
On Mon, Jan 23, 2017 at 12:49:19AM -0200, Soni L. wrote: [...]
You seem to be thinking of "immutable object builder". Not "the builder itself is immutable and operations on it create new builders".
Why would you make a builder class immutable? That's not a rhetorical question -- I'm genuinely surprised that you're not only using the builder design pattern (there are usually better solutions in Python) but more so that you're making the builder itself immutable. In any case, it seems that this is such a narrow and unusual use-case that it wouldn't be wise to give it special syntax. Especially when there are other, potentially far more useful, uses for the .= syntax, e.g. Julia's syntactic loop fusion: http://julialang.org/blog/2017/01/moredots -- Steve
![](https://secure.gravatar.com/avatar/3a304881dc609b0382ec45d20b7ae9c5.jpg?s=120&d=mm&r=g)
On 23/01/17 11:54 AM, Steven D'Aprano wrote:
On Mon, Jan 23, 2017 at 12:49:19AM -0200, Soni L. wrote: [...]
You seem to be thinking of "immutable object builder". Not "the builder itself is immutable and operations on it create new builders". Why would you make a builder class immutable?
Builders for network connections where you don't wanna start with a fresh builder every time.
That's not a rhetorical question -- I'm genuinely surprised that you're not only using the builder design pattern (there are usually better solutions in Python) but more so that you're making the builder itself immutable.
In any case, it seems that this is such a narrow and unusual use-case that it wouldn't be wise to give it special syntax. Especially when there are other, potentially far more useful, uses for the .= syntax, e.g. Julia's syntactic loop fusion:
It is far more useful only because it's not just a syntax sugar. It's more like a completely new, standalone operator. Which, IMO, makes it more confusing. I propose `x .= y` -> `x = x . y`, for any `y`. You propose `x .= y` -> `operate_on(x).with(lambda: y)` This feels like you're arguing for loops are more useful than multiplication, and thus we shouldn't have multiplication.
![](https://secure.gravatar.com/avatar/3dd475b8aaa5292d74cb0c3f76c3f196.jpg?s=120&d=mm&r=g)
On Mon, Jan 23, 2017, at 09:12, Soni L. wrote:
Builders for network connections where you don't wanna start with a fresh builder every time.
Maybe you need a builder builder. Or, more seriously, a way to differentiate the things in the 'builder' from the things that are going to be different with each connection, and pass them separately in the connection's constructor. Or a clone method.
It is far more useful only because it's not just a syntax sugar. It's more like a completely new, standalone operator. Which, IMO, makes it more confusing.
I propose `x .= y` -> `x = x . y`, for any `y`.
I think you're underestimating the extent to which the fact that "y" isn't a real expression will cause confusion.
![](https://secure.gravatar.com/avatar/214c694acb154321379cbc58dc91528c.jpg?s=120&d=mm&r=g)
On 22 Jan 2017, at 22:45, Soni L. <fakedme+py@gmail.com> wrote:
This pattern is present in the cryptography module already with things like their x509.CertificateBuilder: https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certific... <https://cryptography.io/en/latest/x509/reference/#cryptography.x509.Certific...>. My 2c, but I find that code perfectly readable and legible. I don’t think a dot-equals operator would be needed. Cory
![](https://secure.gravatar.com/avatar/d995b462a98fea412efa79d17ba3787a.jpg?s=120&d=mm&r=g)
On 22 January 2017 at 22:45, Soni L. <fakedme+py@gmail.com> wrote:
I've been thinking of an Immutable Builder pattern and an operator to go with it. Since the builder would be immutable, this wouldn't work:
long_name = mkbuilder() long_name.seta(a) long_name.setb(b) y = long_name.build()
Instead, you'd need something more like this:
long_name = mkbuilder() long_name = long_name.seta(a) long_name = long_name.setb(b) y = long_name.build()
Or we could add an operator to simplify it:
long_name = mkbuilder() long_name .= seta(a) long_name .= setb(b) y = long_name.build()
(Yes, I'm aware you can x = mkbuilder().seta(a).setb(b), then y = x.build(). But that doesn't work if you wanna "fork" the builder. Some builders, like a builder for network connections of some sort, would work best if they were immutable/forkable.)
I don't think the .= operator adds enough to be worth it. If the problem you see is the duplication of long_name in those lines (it's difficult to be sure without a real example) then you can use a temporary: b = mkbuilder() b = b.seta(a) b = b.setb(b) long_name = b y = long_name.build() For your real example: On 22 January 2017 at 23:30, Soni L. <fakedme+py@gmail.com> wrote:
fnircbotbuilder = mkircbotbuilder(network="irc.freenode.net", port=6697, ssl=true) mainbot = mkircbotbuilder(parent=fnircbotbuilder, # ??? channels=["#bots"]).build() fndccbotbuilder = mkircbotbuilder(parent=fnircbotbuilder, dcc=true, channeldcc=true) dccbot = mkircbotbuilder(parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherircbotbuilder = mkircbotbuilder(parent=fndccbotbuilder, network="irc.subluminal.net") # because we want this whole network otherbot = mkircbotbuilder(parent=otherircbotbuilder, channels=["#programming"]).build() # to use DCC and channel DCC
But this would be cleaner:
botbuilder = mkircbotbuilder().network("irc.freenode.net").port(6697).ssl(true) mainbot = botbuilder.channels(["#bots"]).build() botbuilder .= dcc(true).channeldcc(true) dccbot = botbuilder.channels(["#ctcp-s"]).build() botbuilder .= network("irc.subluminal.net") otherbot = botbuilder.channels(["#programming"]).build()
I don't find the second example appreciably cleaner than the first. But a bit of reformatting looks better to me: # First create builders for the bots fnircbotbuilder = mkircbotbuilder( network="irc.freenode.net", port=6697, ssl=true) fndccbotbuilder = mkircbotbuilder( parent=fnircbotbuilder, dcc=true, channeldcc=true) otherircbotbuilder = mkircbotbuilder( parent=fndccbotbuilder, network="irc.subluminal.net") # Now create the actual bots mainbot = mkircbotbuilder( parent=fnircbotbuilder, channels=["#bots"]).build() dccbot = mkircbotbuilder( parent=fndccbotbuilder, channels=["#ctcp-s"]).build() otherbot = mkircbotbuilder( parent=otherircbotbuilder, channels=["#programming"]).build() And some API redesign (make the builders classes, and the parent relationship becomes subclassing, and maybe make channels an argument to build() so that you don't need fresh builders for each of the actual bots, and you don't even need the "builder" in the name at this point) makes the whole thing look far cleaner (to me, at least): class FNIRCBot(IRCBot): network="irc.freenode.net" port=6697 ssl=True class FNDCCBot(FNIRCBot): dcc=True channeldcc=True class OtherIRCBot(IRCBot): network="irc.subluminal.net" mainbot = FNIRCBot(channels=["#bots"]) dccbot = FNDCCBot(channels=["#ctcp-s"]) otherbot = OtherIRCBot(channels=["#programming"]) Paul
participants (10)
-
Cory Benfield
-
Ethan Furman
-
Joao S. O. Bueno
-
M.-A. Lemburg
-
Paul Moore
-
Random832
-
Serhiy Storchaka
-
Soni L.
-
Steven D'Aprano
-
Wes Turner