Puzzling Scala 3.2 warnings

After upgrading to Scala 3.2, I get warnings like this one:

warn] -- Warning: /home/informarte/Workspaces/yuck/src/main/yuck/flatzinc/compiler/NeighbourhoodFactory.scala:78:30 
[warn] 78 |            (for ((objective, Some(neighbourhood)) <- buf) yield (objective, neighbourhood)).unzip
[warn]    |                              ^^^^^^^^^^^^^^^^^^^
[warn]    |pattern's type Some[yuck.core.Neighbourhood] is more specialized than the right hand side expression's type Option[yuck.core.Neighbourhood]
[warn]    |
[warn]    |If the narrowing is intentional, this can be communicated by adding the `case` keyword before the full pattern,
[warn]    |which will result in a filtering for expression (using `withFilter`).
[warn]    |This patch can be rewritten automatically under -rewrite -source 3.2-migration.

I don’t understand this warning.

The line is intended to loop those pairs where the second value is defined. It has always worked, it still works according to the unit test results, and the behavior is as described in the Scala book which states that

for (pat <- expr 1 ) yield expr 2

translates to

expr 1 filter {
  case pat => true
  case _ => false
} map {
  case pat => expr 2
}

Regarding the suggestion to add case,

(for (case (objective, Some(neighbourhood)) <- buf) yield (objective, neighbourhood)).unzip

does not compile due to an unspecified syntax error.

Could someone shed some light on this warning?

Thank you!

Could this be relevant?

Scala 3.2.1 is now available!

This version is a bug-fixing release focusing mainly but not exclusively on solving rare issues with code not compiling after upgrading the language version to 3.2.0.

I have no idea about the warnings.

I already tried 3.2.1, but it does not make a difference.

This change is described on this page. The plan seems to be to disable filtering for comprehensions without case in 3.3, so in 3.2 you only get the warning and the chance to migrate.

The code you gave with case does compile for me, so I’m not sure what the problem is there.

It doesn’t really seem to be explained on the doc page, but I think the reason for this change is that in the future you should be able to use destructuring expressions in for comprehensions without withFilter as long as they can’t fail.

val list = List(1 -> 2, 2 -> 3, 3 -> 4)

for (a, b) <- list yield a + b

// in scala <=3.2:
list.withFilter {
  case (a, b) => true
  case _ => false
}
.map{ case (a, b) => a + b }

// in scala >=3.3:
list.map{ case (a, b) => a + b }
3 Likes

That’s also my loose understanding.

I did see tickets in this area, such as this one.

I didn’t try these examples, but possibly there is a messaging regression with some combination of versions and flags.

Context that may not be obvious to folks who haven’t been bitten by this: this sort of filtering is a common source of mystifying bugs, with data being silently thrown away by accident.

I assume that that’s the reason for this change – it tells you if you’re potentially going to do something dangerous, and requires you to make explicit that you intend to do that.

2 Likes

Two things tnat I find confusing: First, the warning itself:

It sounds like there will no filtering without case. While this seems to be the intended behavior for future Scala versions, Scala 3.2.1 filters even without case.

Second, the documentation of pattern bindings does not talk about the way forward. Will it be possible to destructure expressions in for comprehensions even when they could fail? (That would actually be a new and very useful feature.) If yes, would there be a warning? And if so, would there be a way to suppress it, say with @unchecked?

1 Like

@Jasper-M, regarding my compilation issues:

  • It seems that, in some contexts, the Scala plugin of IDEA has a problem with case in for comprehensions (resulting in wrong highlighting) although the code compiles.
  • It seems that mill does not restart its compilation server upon change of the Scala version. (The next day, after restarting my computer, the command-line build worked.)

Indeed! I had no idea this implicit filtering step existed, but I’m glad it’s going away.