-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Nothing warns about unconsumed values in for comprehension #18854
Comments
As a note, fixing this would be a huge improvement in purely functional codebases (CE/ZIO) - this failure mode is responsible for quietly missed log statements, assertions that aren't actioned, concurrency bugs, etc. |
Scala 2.13 doesn't warn either:
@som-snytt probably knows if there's a Scala 2 ticket on that |
I will look at that after I finish the current couple of tasks on linting. |
Closing this issue as not a bug, because the purpose of this syntax ( |
Trees at typer for scala 2 & 3:
and
That is for
where the To notice it is unused, you must fast-forward to the map where the pattern var is unused; but it may be marked synthetic? I no longer remember why it is not marked unused. The scala 2 umbrella ticket (for improved diagnostics of for comprehensions) is scala/bug#10287. When the "improved for comprehension" PR is merged, we'll need "improved diagnostics for improved for comprehensions". |
I see there was a question mark in the expectation section, so I broadened the title to survive legal challenge. |
Scala 2 manages to warn, but Scala 3 does not yet.
Scala 2 could win on "points", as they say in boxing (the sport, not the value class conversion). |
The specific issue I was referring to arises with the use of
cases? the x and z are used in the desugared code (see below) in order to pass them along to subsequent calls to map, so would need to special case their treatment depending on if those variables, in an earlier phase, had been within a for comprehension. Desugared:
|
From a user's perspective, as both x and z are no longer valid after the scope of The specific expansion of for comp is an implementation detail and IMO doesn't justify lack of warning – from a philosophical point of view, warning on unused named values and variables is designed to help with local reasoning - "if this thing has no name, that means it cannot be used in any later part of the code, so I can just not think about it anymore" |
From what we discussed with the team and what I understood, there are 2 distinct issues. Warning about unused valuesAs described in @som-snytt's comment: for (x <- List(1); y = x) do println(x) // should warn about unused y This is definitely a shortcoming of There is an existing issue for this: #18289. Warning about "unconsumed" valuesAs described in this issue's description, it seems that it would be useful for some uses-cases to have a warning when "assigning" a non-unit value to def foo(): Int = 42
for x <- List(1); _ = foo() do println(x) // should warn about `foo()` returning non-unit? However, as @aherlihy noted, we don't and probably don't want to warn when assigning to val _ = foo() // no warning So why should it be the case in a for-comprehension? Why should the right hand-side of an assignment to |
The warning about |
It is a specification detail. After spending some time with the elaboration Scala 2 for comps, or belaboration would be a better word, I am sympathetic to keynmol's request, and I'll try to motivate it (at a later date). It would be nice if we had valdef syntax still, to mean "I intend to discard a value, not just to omit naming it."
The difference is that midstream assignment always gets a name (per spec).
That is because the value is always packed in the tupling step, even though when untupled it is underscore (ignored). Note that |
In the spirit of the original issue, I think the problem @keynmol had in mind when writing about def log(msg: String): IO[Unit] = ???
for
res1 <- doStuff() // IO[Result]
_ = log(s"Did stuff and got ${res1.output}") // silently discarded IO[Unit]
res2 <- doOtherStuff() // IO[AnotherResult]
yield (res1, res2) // IO[(Result, AnotherResult)] Maybe this would be more helpful? |
Compiler version
3.3.1+ (tested on nightly as well)
Minimized code
This shows several places where it works and the one where it doesn't
Output
// TODO add output here
Expectation
I expected a warning because it's a non-unit statement (?).
The text was updated successfully, but these errors were encountered: