Readit News logoReadit News
tialaramex · a month ago
String interpolation is one of those features like inference where if you've had it before then going without is very annoying, and so you add some and that's nicer, then you add more, each step seems like it's an improvement, and then one day you realise you're looking at unintelligible nonsense and you say "Oh no, what have we done?"

This is unusual, often CS would love to have as much of whatever as we can, but mathematics says no that's literally or practically impossible - but here both none and lots are awful and shouldn't be permitted.

One option, which Python and C# both picked is, well, leave it to taste. You can write sixteen pages of layered expressions in the uncommented interpolated string and it'll work, but your colleagues will curse your name and plot your destruction. Or at least you'll fail code review if you enforce such things.

Another option, in standard C++ 23 today for example, is refuse to take even the first step. You can have rich formatting, but standard C++ does not provide interpolation at all, if you want to format six parameters then pass them as parameters.

I'm happy with Rust's "Only a tiny bit of interpolation" where you can interpolate only identifiers, not any other expressions, but that's definitely more interpolation than some will be happy with, yet of course in some cases it's not quite enough.

Waterluvian · a month ago
Purity and practicality are at odds and every language finds a different balance between the two. There is no one correct balance so busy minds will inevitably have loud opinions they want accepted as the one correct balance.
almostgotcaught · a month ago
> busy minds

cute pun but compared to busybodies it really hides the implication (i thought you were talking about people with adhd at first).

orthoxerox · a month ago
> One option, which Python and C# both picked is, well, leave it to taste.

Every time I need to format a number or a date in C#, I just hit the documentation. I refuse to memorize its terrible formatting mini language.

Timwi · a month ago
Or you could just write your own if you think you can create one you'll be happier with. C# provides IFormatProvider for this exact purpose and I've honestly not seen that level of customizability elsewhere.
almostgotcaught · a month ago
> String interpolation is one of those features

70% of these "wtfs" aren't about string interpolation but just python's syntax for string.format

https://docs.python.org/3/library/string.html#format-string-...

zahlman · a month ago
"String interpolation" is a term for the purpose that the string.format syntax serves.
whartung · a month ago

  > then one day you realise you're looking at unintelligible nonsense and you say "Oh no, what have we done?"
I think that’s a bit of what the Java String Template folks went through.

It was a pretty cool system (on the surface, not having used it, I liked what I saw), but they pulled it straight out. They felt the direction was unworkable. Pretty interesting considering I think the demand for such a facility (as mentioned, once tasted, hard to let cider), plus the work involved to date. Interesting they found it irredeemable to shelve it entirely and back to the white board.

PaulHoule · a month ago
I tend to stuff complicated strings (SQL queries and such) into resources (files that get baked into the JAR) and implement some kind of templating for them if I think it's necessary.
veber-alex · a month ago
I hate Rust's solution because it's not a solution at all.

Interpolation only works in a small subset of cases, which makes you constantly having to think whether it can or can't be used in the current situation and requires endless churn when refactoring code.

At the very minimum, they need to allow it to work with field access.

On the other hand, in python, while examples like this site exist and are funny/weird/quirky in practice nobody cares, and they just enjoy using fstrings.

Philpax · a month ago
It's pretty straightforward? You can interpolate a variable in scope and apply modifiers to it. You can't interpolate arbitrary expressions (which field access would be). Alternatively, you can interpolate an arbitrary identifier, then specify a value for that identifier in the arguments.

The key is "identifiers, not expressions."

zahlman · a month ago
> then you add more, each step seems like it's an improvement, and then one day you realise you're looking at unintelligible nonsense and you say "Oh no, what have we done?"

I've never found it difficult to keep them in check. I still can't fathom a use case for nesting f-strings within an f-string substitution, for example. And probably the only format specifier I commonly use is `:02x`.

Doxin · a month ago
> I still can't fathom a use case for nesting f-strings within an f-string substitution

Isn't that mostly just a side effect of using the proper full python parser for F-strings? The syntax for what was allowed inside an F-string kept getting more and more permissive, until they just decided to permit ~all of python inside an F-string.

sublinear · a month ago
> CS would love to have as much of whatever as we can, but mathematics says no

What does this have to do with either topic?

tialaramex · a month ago
Take optimisation, another task like interpolation which the machine does to transform our program text into an executable

We'd like exhaustive optimisation, in fact that can't be done and today we're used to a relatively aggressive optimisation which would not have been possible fifty years ago, but we're nowhere close to optimal for non-trivial programs.

Or correctness, it seems as though global static analysis should be possible, at least for something modest like a typical Unix utility - nope, that is known to be mathematically impossible for even quite modest software, only local analysis is at least plausible and its effects are much more limited than we might hope.

cvoss · a month ago
I, too, struggled to understand the meaning of this sentence. I can parse it, I guess, but any meaning I try to guess for it is either implausible or irrelevant.
ejiblabahaba · a month ago
Learned a few tricks that I'm sure are buried on fstring.help somewhere (^ for centering, # for 0x/0b/0o prefixes, !a for ascii). I missed the nested f-strings question, because I've been stuck with 3.11 rules, where nested f-strings are still allowed but require different quote characters (e.g. print(f"{f'{{}}'}") would work). I guess this got cleaned up (along with a bunch of other restrictions like backslashes and newlines) in 3.12.

F-strings are great, but trying to remember the minute differences between string interpolation, old-style formatting with %, and new-style formatting with .format(), is sort of a headache, and there's cases where it's unavoidable to switch between them with some regularity (custom __format__ methods, templating strings, logging, etc). It's great that there's ergonomic new ways of doing things, which makes it all the more frustrating to regularly have to revert to older, less polished solutions.

sfoley · a month ago
Yeah I consider that one to be a trick question. I knew same-quote-style nested f-strings were coming, I just didn't know which version, and I still use the `f'{f"{}"}'` trick because I want my code to support "older" versions of python. One of my servers is still on 3.10. 3.11 won't be EOL until 2027.
zahlman · a month ago
> I guess this got cleaned up (along with a bunch of other restrictions like backslashes and newlines) in 3.12.

Yes: https://docs.python.org/3/whatsnew/3.12.html#whatsnew312-pep...

ctoth · a month ago
One thing I keep not understanding is I see colleagues or AIs put f-strings in logger calls! Doesn't the logger do lazy interpolation? Doesn't this lose that nice feature?
mixmastamyk · a month ago
Yes, but won’t matter unless in a tight loop. I usually use % with logging but once in a while use f’’ if easier or needed for some reason.
nojs · a month ago
> This is the first special feature of f-strings: adding a trailing equals sign lets you print out the expression and what it evaluates to.

    >>> foo='bar'; print(f"{foo=}")
    foo='bar'
Wow, never knew you could do that.

ck45 · a month ago
Python release notes are really worth reading, for me there's usually some "positive surprise"

The = support was added in Python 3.8: https://docs.python.org/3/whatsnew/3.8.html#f-strings-suppor...

carlhjerpe · a month ago
If you come from verbosity land C# release notes are magically good as well, always some way to reduce boilerplate while maintaining "implicit verbosity" which your proprietary LSP resolves 100% correctly.

I'd prefer writing C# if I had the Linux interaction libs Python has. I'm too dumb to write syscall wrappers

acdha · a month ago
It makes me sad that the PEP for the equivalent behaviour for function keyword arguments wasn’t accepted. It’s really common to see foo(bar=bar) and I think it’s not only cleaner but would help see subtle differences if that was foo(bar=) because it would make the cases where some arguments aren’t simply being passed through more obvious: foo(bar=, …, baaz=baaz.get_id()) avoids the most interesting detail being easily missed.
carlhjerpe · a month ago
Do you know why? I didn't know of the fstring one either but I've thought to myself across many languages that a way to print the expression (or just varname in my head) with the result should exist.
roenxi · a month ago
And it seems like a bad idea because of that wow factor - it isn't really adding enough to justify having surprising behaviour. It is more likely to be a bug than a feature.

It'd be better to just let people implement their own function that prints a subset of locals(), or provide a standard function that does the same.

ahartmetz · a month ago
Incredibly common for debug output. In C++, I have made it a habit to just copy the expression, once with quotes and once without. It's informative and doesn't require thinking, or, well, I'm still working on that.
stkdump · a month ago
It's the kind of thing you do with macros in C++.
black_puppydog · a month ago
such a boon for print(f) debugging. :)
jszymborski · a month ago
No more will I have to

print("foo", foo)

pansa2 · a month ago
Despite the URL, I’d only consider a few of these to be WTFs. Questions 20 & 21 definitely are, though:

  >>> a = 42
  >>> print(f"{a:=10}")
          42
  >>> print(f"{(a:=10)}")
  10
I still can’t believe anyone thought the walrus operator was a good idea.

sfink · a month ago
You can pry the walrus operator from my cold dead hands.

    m = re.match(pattern1, line)
    if m:
        do_stuff(m.group(1))
    else:
        m = re.match(pattern2, line)
        if m:
            do_other_stuff(m.group(2))
        else:
            m = re.match(pattern3, line)
            if m:
                do_things(m.groups())
            else:
                m = ...
Obviously, there are ways to improve this particular example without using the walrus. But it's also usually not this simple.

I run into something like this all the time. Not many times per program, and I don't actually use the walrus all that often, but when it's the right thing it's so very nice.

Veedrac · a month ago
FWIW another way to handle this is a local function with early returns. In this example you can also just use a single regex and most likely win performance too.

:= is so common in other languages, or even other Python statements, that it doesn't bother me at all, but Python is such an imperative-first language that its presence is rarely felt.

pansa2 · a month ago
The walrus operator hardly changes that example at all:

    if m := re.match(pattern1, line):
        do_stuff(m.group(1))
    else:
        if m := re.match(pattern2, line):
            do_other_stuff(m.group(2))
        else:
            if m := re.match(pattern3, line):
                do_things(m.groups())
            else:
                m = ...
I've found `:=` useful in the headers of `while` loops, but that's about it. The complexity that walrus adds to the language far outweighs its usefulness.

callc · a month ago
Even though I’ve never used python walrus, I’m a huge fan of the “X language feature can be used as an expression”

I feel the same way with rust control flow being used as an expression, and am disappointed when other languages don’t have it.

Even though one could argue it makes things more complicated, I feel more mental freedom and feel the language getting out of the way.

marcosdumay · a month ago
You know, creating functions is allowed in Python. As are loops.

The assignment is the least problematic thing on that code. I do even disagree on it being a problem.

almostgotcaught · a month ago
this has nothing to do with the walrus operator and everything to do with how string.format works in python https://docs.python.org/3/library/string.html#format-string-...

Edit: someone downvoted me because they don't understand there is no walrus operator here

    print(f"{a:=10}")

lyu07282 · a month ago
They obviously meant the second example:

     >>> print(f"{(a:=10)}")

Ezhik · a month ago
I learned a bunch of these when trying to make an f-string-like library for Lua [1], but `f"{...}" and the walrus ones caught me off-guard.

Glad this is nowhere near Wat [2], though.

[1]: https://ezhik.jp/f-string.lua/

[2]: https://www.destroyallsoftware.com/talks/wat

Timwi · a month ago
There are many mistakes in the Wat talk from destroyallsoftware. The biggest one is that he types `{}+[]` and claims that he's adding an object and a list. It's actually an empty scope followed by a unary + applied to a list. You can convince yourself of that by noting that `({}+[])` gives different output.

A smaller mistake that I find nonetheless amusing is that he uses Array(16) hoping to get 16 string separators. Oops, off by one error :)

Ezhik · a month ago
Arguably that first one is just substituting one Wat for another.
variadix · a month ago
That library looks awesome
Twey · a month ago
As someone who hasn't written Python in anger since before f-strings were a thing, I correctly guessed almost all the f-string specific syntax but made a bunch of errors that were just to do with the return values of various expressions. Maybe f-strings are the least wtf thing about Python? :)
cluckindan · a month ago
If this was JavaScript syntax, most of the comments would be lamenting the unintuitive syntax and weird features.
crazygringo · a month ago
Right, I assumed the real point the quiz was making is that Python is as full of footguns as JavaScript, since I've seen this type of thing for JS a bunch of times.

Not saying I agree, but was definitely expecting that to be the main topic of discussion here...

va1a · a month ago
More specific shenanigans aside, JavaScript will always be the king of unintuitive syntax. Some of these f-string tidbits are very much strange, but you'd have to be implementing something specific to encounter them. Meanwhile over in JS you're still waiting for your dependencies to install so you can compare two arrays.
cluckindan · a month ago
That is exactly the kind of unfounded, strawman-riddled criticism I was after ;-)

What does an algorithmic task such as array comparison have to do with language syntax? The answer is nothing.

Sure, some languages might have builtins for comparing certain things, or doing intersections/differences, but those only apply to arrays of primitives, and even in those cases the utility of the builtins completely depends on the use case, and those builtins still have nothing to do with syntax.

somat · a month ago
On the subject of javascript template literals.

Do they really not support the equivalent to

    let template = 'hello ${name}';
    let n1 = template.format({ 'name':'joe' });
    let n2 = template.format({ 'name':'bob' });
    
I am not really a javascript programmer. but I was writing some and wanted to store a bunch of templates then fill them out dynamically and was unable to figure out how to do it with the native javascript template system. So I ended up having to write my own. As it appears to be a huge obvious hole in missing functionality, I have to ask. Am I holding it wrong?

While writing this up I was rereading the mdn page on template literals, perhaps tagged templates would do what I wanted, I don't remember why I did not use them(I think it is still not possible to store the template), but on first glance, yeah, lamentations about the unintuitive syntax and weird features.

cluckindan · a month ago
Tagged templates probably won’t help, or will be quite messy. But you can just use a thunk with some parameter destructuring:

    const tpl = ({ name }) => 
      `hello ${name}`;
    const n1 = tpl({ name: "joe" });
    const n2 = tpl({ name: "bob" });
Or if you don’t like the duplication of property names, you could do it without destructuring:

    const tpl = (o) => 
      `hello ${o.name}`;
Look for the free online book ”You don’t know JS”, it is good reading.

wolfgang42 · a month ago
I think what you want is to just make a function and pass it the values:

  const template = ({name}) => `hello ${name}`;
  const n1 = template({ name: 'joe' });
(Tagged templates won’t help here because the bit in curly braces is an expression which is evaluated to a value first; you’d need some kind of macro system to get access to the original variable name at runtime.)

jvolkman · a month ago
But if it were Perl, they'd be celebrating.
ziml77 · a month ago
I don't really think there's anything "wtf" worthy about this. A lot of it isn't even about f-string behavior but about the "mini-language" for str.format()
0cf8612b2e1e · a month ago
There were a few wats in there, but I agree that most of the test was if you know all of the string formatting syntax.