I’m not sure I fully understand what you are proposing but from my understanding it seems like it should still be possible. There are different aspects to what counts as “lazy” here so perhaps I am thinking about a lazy f-string as meaning something different from what you are referring to.
Let me give a concrete example for where I would want a lazy f-string. In SymPy it is quite common to have a function that does something like:
def some_func(expression):
if some_condition(expression):
raise ValueError(f"does not work for {expression}")
...
When the exception message is shown in a traceback it is usually nice to see the expression like x**2 + 1 that caused the exception. The problem then is that expression here means a symbolic expression and those can be large. Converting the expression to a string can therefore be expensive (in some contexts the string representation grows exponentially).
It can also then be possible that some other code catches this exception in a loop like:
for expression in expressions:
try:
results.append(some_func(expression))
except ValueError:
results.append(other_func(expression))
Now our expensive string conversion is happening over and over in a loop and the string is always discarded. In most cases the expression is not super large but it can still be wasted CPU cycles to make the string representation and I have found cases where profiling revealed this to be the dominant cost of a higher-level operation.
The problem though is not that f"{expression}" eagerly retrieves the variable expression from the local namespace. Rather the problem is that it eagerly converts the expression into a string like str(expression). We don’t want to convert the expression into a string eagerly if the string is never going to be displayed.
There are of course ways round this like old format strings:
class MyValueError(ValueError):
...
def __str__(self):
return format(self.format_string, *self.args, **self.kwargs)
raise MyValueError("does not work for {0}", expression)
We had those before f-strings came along though and people still put loads of work into making f-strings because they are a bit nicer.
My understanding here is that I should be able to do something like:
raise MyValueError(lazy"does not work for {expression}")
Here the tag string eagerly retrieves the object expression from the namespace but the lazy tag implementation can lazily convert that object to a string later if the string representation is needed.
Are you saying that this kind of lazy f-string would not be possible?
Am I misunderstanding what it is that you mean by a “lazy f-string” and what it is wanted for?