Как предотвратить такой тип соответствия шаблонов ошибок и Nil

Когда вы снова сопоставляете шаблоны списков, вы можете использовать Nil для проверки пустого списка. Однако, если базовый тип является Iterable, вы все равно можете проверить Nil, и он будет разбит на пустые наборы и т.д. См. Следующий сеанс REPL:

scala> val l: Iterable[Int] = List()
l: Iterable[Int] = List()

scala> l match {
     | case Nil => 1
     | case _ => 2
     | }
res0: Int = 1

scala> val l: Iterable[Int] = Set() 
l: Iterable[Int] = Set()

scala> l match {
     | case Nil => 1
     | case _ => 2
     | }
res2: Int = 2

Вопрос: как я могу предотвратить такую ​​проблему? Очевидно, что если l является списком типов, это не ошибка. И если l имеет тип Set, он не будет компилироваться. Но что, если у нас есть класс, который имеет список, определите функцию, которая соответствует шаблону таким образом, а затем кто-то изменит класс, чтобы вместо этого использовать общий итеративный вариант? Является ли этот шаблон Nil vs. _ плохой идеей?

Ответ 1

Преобразуйте scrutinee в список, чтобы устранить сомнения.

l.toList match {
  case Nil => 1
  case xs  => 2
}

Ответ 2

Одна из возможностей - использовать охрану:

scala> val xs: Iterable[Int] = Set()
xs: Iterable[Int] = Set()

scala> xs match { case xs if xs.isEmpty => 1 case _ => 2 }
res0: Int = 1

Другой способ сделать это - использовать выражение if-else (лучше всего работает, если у вас есть только одно или два условия для проверки):

scala> if (xs.isEmpty) 1 else 2
res1: Int = 1

Ответ 3

Вот еще один вариант (каламбур):

scala> val l: Iterable[Int] = List()
l: Iterable[Int] = List()

scala> l.headOption match { case None => 1; case Some(h) => 2 }
res0: Int = 1

Это полезно в случаях, когда вы сопоставляете шаблоны, чтобы получить head как в популярном List() match { case h :: t => ... }, но это не список, он Iterable и :: не сможет.

Я добавил этот ответ, потому что я подумал, что это довольно часто встречается в шаблоне для коллекции, чтобы получить голову, иначе вы можете просто проверить с помощью xs.isEmpty.