[Python-ideas] "Immutable Builder" Pattern and Operator

Paul Moore p.f.moore at gmail.com
Mon Jan 23 04:54:55 EST 2017


On 22 January 2017 at 22:45, Soni L. <fakedme+py at 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 at 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


More information about the Python-ideas mailing list