Open Myzel394 opened 2 years ago
Sounds like you're looking for the readability of variables but without the technical overhead of variables. Someone correct me if I'm wrong, but I assumed that using final
would allow the compiler to optimize the variable away if possible.
I don't actually see why the "optimal code for efficiency" is any more efficient than the "optimal code for humans".
The output from a moderately competent compiler should be indistinguishable. (I hope our compilers are moderately competent, but with async
, I'm not promising anything.)
What you are describing looks remarkably like late
final
variables - except that late variable initializers can't contain await
, so you can't use those here.
They can't contain await
for a good reason, so the asynchronous placeholders allowing it is itself worrisome. It would be asynchronous expressions in a seemingly non-asynchronous piece of code. The Result(location: location, speed: speed)
doesn't look like it contains two await
s. Users won't necessarily be able to figure out which order things happen in.
So, the "for humans" code might not actually be good for human readability, if figuring out evaluation order is important.
@lrhn Yes it's like when you use final
with a good compiler. Although I don't know if a compiler will also efficiently optimize cases when you use the variable multiple times.
Example:
final Location location = await Getter.getLocation();
final Speed speed = await Getter.getSpeed();
final Result result = await Result(
location: location.
speed: speed,
);
final AnotherResult anotherResult = await AnotherResult(
location: location
);
Will this force the compiler to create an extra variable for location
and efficiently place speed
into the Result
construction?
Evaluation order matters, so the compiler will definitely evaluate location
before speed
and both before calling Result
.
In compilation, evaluation order is pretty much the only thing which matters, which is why
final Location location = await Getter.getLocation();
final Speed speed = await Getter.getSpeed();
final Result result = await Result(
location: location.
speed: speed,
);
and
final Result result = await Result(
location: await Getter.getLocation().
speed: await Getter.getSpeed(),
);
should be indistinguishable. It's not that the compiler puts await Getter.getSpeed()
into the Result
constructor call, instead it throws away all the source code and generates some native code with the same effect. Since the two pieces of code has exactly the same effect, there result can be the same.
Variables do not exist at runtime, it's a source concept that allows the source code to refer to the same thing from different places. How those variables are implemented is entirely up to the compiler.
Adding
final AnotherResult anotherResult = await AnotherResult(
location: location
);
after the first call just means that the value stored in the location
variable needs to be alive at the other call too. The compiler can store it on the stack or in a persistent register, the only thing it can't do is throw away the value before it has been used again.
It doesn't introduce an extra variable, it extends the lifespan of the value computed by await Getter.getLocation()
to extend past the call to AnotherResult
, and the lets the compiler backend find a way to keep that value around.
(So it requires at least one register or memory location more than the earlier example to get the value past the await Result(..)
computation.)
@lrhn thanks for the detailed answer! :heart:
So it requires at least one register or memory location more than the earlier example to get the value past the await Result(..) computation.
Here's where my proposal of placeholders comes in! Instead of using more memory, placeholders will simply recalculate the value.
This is not applicable in every scenario, but there are certainly a lot of times where it'd have been better if an app simply had recalculated a value instead of eating up memory.
Recalculating the value, which can possibly be an asynchronous computation, sounds very error-prone.
If you want to just share the code, there is always functions:
Location location() async => await Getter.getLocation();
final Result result = await Result(
location: await location().
speed: await Getter.getSpeed(),
);
final AnotherResult anotherResult = await AnotherResult(
location: await location()
);
The big issue here is that you need to write await
again, because we can't abstract over asynchrony.
And it requires the compiler to efficiently inline the function for it to be as efficient (and be smart about the double await
).
So, really, what a placeholder is, is more like a local getter:
Location get location => await Getter.getLocation();
but one which can only be used internally in the same function, is guaranteed to be inlined, and can therefore use the same asynchrony as the surrounding function.
Seems too complicated for what it brings to the table :)
Instead of using more memory, placeholders will simply recalculate the value.
This is also not a universally accepted tradeoff. For example, if you have a function that computes a number, or a function that makes a network call to get an object, these are values most people would want to be cached (ie, trading off memory for time). If you want to pass around the instructions to compute the value instead of the value itself, use a function as in @lrhn's example because that's what functions are for.
Use case
I found out that I'm often creating a variable for clarification instead of writing it in one line.
Example (pseudo code):
Optimal code for humans
Optimal code for efficiency
While the given example is admittedly quite readable, there are often cases where it's easier to create a variable for clarification.
Proposal
It would be quite nice if you could use named "placeholders" that replace code parts during compilation.
Example:
After compilation this code will result in:
Advantages to the current Flutter version
You don't have to create variables just for better DX (Developer Experience). This will primarily lead to less memory usage, which means there will be more memory available, which means faster, better and more efficient apps! 😆