Help getting rid of match may not be exclusive warning

I’m getting an warning which I don’t know how to stifle. Can someone help me understand exactly what the warning is telling me and how to fix it?

Warning:(159, 67) match may not be exhaustive.
It would fail on the following inputs: (((_ : Int), (_ : Int)), (_, (_ : Int), _)), (((_ : Int), (_ : Int)), (_, _, (_ : Int))), (((_ : Int), (_ : Int)), (_, _, _)), (((_ : Int), (_ : Int)), _), (((_ : Int), _), (_, (_ : Int), (_ : Int))), (((_ : Int), _), (_, (_ : Int), _)), (((_ : Int), _), (_, _, (_ : Int))), (((_ : Int), _), (_, _, _)), (((_ : Int), _), _), ((_, (_ : Int)), (_, (_ : Int), (_ : Int))), ((_, (_ : Int)), (_, (_ : Int), _)), ((_, (_ : Int)), (_, _, (_ : Int))), ((_, (_ : Int)), (_, _, _)), ((_, (_ : Int)), _), ((_, _), (_, (_ : Int), (_ : Int))), ((_, _), (_, (_ : Int), _)), ((_, _), (_, _, (_ : Int))), ((_, _), (_, _, _)), ((_, _), _), (_, (_, (_ : Int), (_ : Int))), (_, (_, (_ : Int), _)), (_, (_, _, (_ : Int))), (_, (_, _, _)), (_, _)
    val (minX:Int, minY:Int) = stationPositions.fold((maxX,maxY)) {

Here is the code. The error occurs on the line which sets the value of minX and minY, but not on the code that sets maxX and maxY. In my actual program the value of stationPositions is a much larger array than shown here with just 3 entries.

object TestingMetroMap {
  def main(argv:Array[String]):Unit = {
    val stationPositions:Array[(String,Int,Int)] = Array(
      // station-name, x-position, y-position, useful for drawing a metro map
      ("Abbesses", 308, 536 ),
      ("Alexandre Dumas", 472, 386 ),
      ("Alma Marceau", 193, 404 ))

    val (maxX: Int, maxY: Int) = stationPositions.foldLeft((0, 0)) {
      case ((maxX, maxY), (_, x, y)) => (math.max(x, maxX), math.max(y, maxY))
    }
    val (minX:Int, minY:Int) = stationPositions.fold((maxX,maxY)) {
      case ((minX:Int, minY:Int), (_, x:Int, y:Int)) => (math.min(x,minX), math.min(y,minY))
    }
    println(List(minX, minY, maxX, maxY))
  }
}

After posting the question, I noticed that the 2nd call is using fold rather than foldLeft. When I changed it to foldLeft, the warning goes away. Indeed foldLeft is what I intended, but I still don’t understand really what the problem was. I believe the code was working correctly.

fold has a different type signature:

def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1

In your case A =:= (String,Int,Int) and A1 has to be a supertype of A. You pass in an argument of type (Int, Int) which is not a supertype. A1 will be inferred as the LUB of both types: Product with Serializable. Because this type is much wider, from the point of view of the type checker that match is indeed not exhaustive.

You would’ve noticed much sooner that something’s not right without all the type ascriptions. This will compile perfectly fine, with minX and minY both inferred as Int:

val (minX, minY) = stationPositions.foldLeft((maxX,maxY)) {
  case ((minX, minY), (_, x, y)) => (math.min(x,minX), math.min(y,minY))
}

While with fold, it would stop compiling without all the type ascriptions in the pattern match (and a type ascription in a pattern match is more or less the same as isInstanceOf and asInstanceOf). And if you leave out the type ascriptions in val (minX:Int, minY:Int) = (which is—again—a pattern) then minX and minY would have type Any.

At runtime it worked because fold just delegates to foldLeft for collections like List or Array. But it won’t work in general because fold can choose to use a different evaluation strategy. For instance the parallel collections would fold multiple chunks in parallel and then fold the results of those folds.

1 Like

Actually without the type declarations it gives a compilation error, no longer a warning. I probably put in the declarations to make it compile.


  val (maxX: Int, maxY: Int) = stationPositions.foldLeft((0, 0)) {
    case ((maxX, maxY), (_, x, y)) => (math.max(x, maxX), math.max(y, maxY))
  }
  val (minX, minY) = stationPositions.fold((maxX,maxY)) {
    case ((minX, minY), (_, x, y)) => (math.min(x,minX), math.min(y,minY))
  }

I get the following compilation errors:

Error:(39, 45) overloaded method value min with alternatives:
  (x: Double,y: Double)Double <and>
  (x: Float,y: Float)Float <and>
  (x: Long,y: Long)Long <and>
  (x: Int,y: Int)Int
 cannot be applied to (Any, Any)
    case ((minX, minY), (_, x, y)) => (math.min(x,minX), math.min(y,minY))
Error:(39, 63) overloaded method value min with alternatives:
  (x: Double,y: Double)Double <and>
  (x: Float,y: Float)Float <and>
  (x: Long,y: Long)Long <and>
  (x: Int,y: Int)Int
 cannot be applied to (Any, Any)
    case ((minX, minY), (_, x, y)) => (math.min(x,minX), math.min(y,minY))

Both of those errors are on the same line, so as @Jasper-M said, the foldLeft will compile, while the fold won’t.