Remix.run Logo
paulddraper 8 days ago

No, the format function doesn't "arbitrarily execute code."

An f/t string is syntax not runtime.

Instead of

    "Hello " + subject + "!"
you write

    f"Hello {subject}!"
That subject is simple an normal code expression, but one that occurs after the opening quote of the literal and before the ending quote of the literal.

And instead of

    query(["SELECT * FROM account WHERE id = ", " AND active"], [id])
you write

    query(t"SELECT * FROM account WHERE id = {id} AND active")
It's a way of writing string literals that if anything makes injection less likely.
mjevans 8 days ago | parent | next [-]

Please read the context of my reply again.

The Rejected Golang proposal cited by the post I'm replying to. NOT Python's present PEP or any other string that might resolve magic variables (just not literally eval / exec functions!).

zahlman 8 days ago | parent | next [-]

As far as I can tell from the linked proposal, it wouldn't have involved such evaluation either. It seems like it was intended to work fundamentally the same way as it currently does in Python: by analyzing the string literal ahead of time and translating into equivalent explicit formatting code, as syntactic sugar. There seem to have been many misunderstandings in the GitHub discussion.

mjevans 8 days ago | parent [-]

In that case, I might have misunderstood the intent of those examples.

However the difficulty of understanding also illustrates the increased maintenance burden and language complexity.

eviks 8 days ago | parent [-]

Unless workarounds to a missing feature have a higher maintenance burden like in this case, and you can't avoid it via learning

mjevans 8 days ago | parent [-]

Go's preferred way would probably be something like compute the aliased operations on the line(s) before, then reference the final values.

E.G. Adapting https://github.com/golang/go/issues/34174

    f := 123.45
    fmt.Fprintln("value=%08.3f{f}") // value=0123.450
    fmt.Fprintln("value=%08.3f", f) // value=0123.450
    s := "value"
    fmt.Fprintln("value='%50s{s}'") // value='<45 spaces>value'
    fmt.Fprintln("value='%50s'", s) // value='<45 spaces>value'
The inline {variable} reference suffix format would be less confusing for situations that involve _many_ variables. Though I'm a bit more partial to this syntax with an immediately trailing %{variable} packet since my gut feeling is that special case would be cleaner in a parser.

    fmt.Fprintln("value=%08.3f%{f}") // value=0123.450
    fmt.Fprintln("value='%50s%{s}'") // value='<45 spaces>value'
paulddraper 8 days ago | parent | prev [-]

The proposal cited Swift, Kotlin, and C# which have similar syntax sugar.

The proposal was for the same.

chrome111 8 days ago | parent | prev [-]

Thanks for this example - it makes it clear it can be a mechanism for something like sqlc/typed sql (my go-to with python too, don't like orms) without a transpilation step or arguably awkward language API wrappers to the SQL. We'll need linters to prevent accidentally using `f` instead of `t` but I guess we needed that already anyways. Great to be able to see the actual cost in the DB without having to actually find the query for something like `typeddb.SelectActiveAccount(I'd)`. Good stuff.

WorldMaker 8 days ago | parent | next [-]

The PEP says these return a new type `Template`, so you should be able to both type and/or duck type for these specifically and reject non-Template inputs.

paulddraper 8 days ago | parent | prev [-]

It is a different type.

You can verify that either via static typechecking, or at runtime.