Пропущен случай сопоставления с шаблоном Scala

#scala #pattern-matching #lift

#scala #сопоставление с шаблоном #поднимите

Вопрос:

Спасибо за отличный пример, я попробовал его, и он работает так, как я ожидал. Приятно видеть, что кто-то понял природу проблемы. Однако, я думаю, мне следовало пометить проблему с помощью Lift, поскольку я использую платформу Lift, и именно там (все еще) возникает эта проблема (хотя я все еще думаю, что это может быть связано с извлечением в scala). Поскольку я не хочу воспроизводить здесь всю настройку Lift, поскольку это будет слишком много кода, я буду надеяться, что кто-то, знакомый с Lift, сможет понять, что я здесь делаю. Я удалил больше переменных, чтобы (для некоторых) было легче увидеть проблему:

 lazy val dispatch: LiftRules.DispatchPF = {
  // Explicitly setting guard to false to trigger the scenario
  case req: Req if false => () => println("shouldn't match"); Empty
  // This should match since previous case will never match
  case Req(_, _, _) => () => println("should match"); Empty
  // This is actually called...
  case _ => () => println("shouldn't reach here"); Empty
}
  

Как и раньше, если я закомментирую первый случай, второй случай будет подобран так, как ожидалось.

Для тех, кому интересно, простое обходное решение:

 lazy val dispatch: LiftRules.DispatchPF = {
  case req: Req => {
    if (false) { // Obviously you put something more useful than false here...
      () => println("shouldn't match"); Empty
    } else req match {
      // This matches
      case Req(_, _, _) => () => println("should match"); Empty
      // This is now never called
      case other => () => println("shouldn't reach here"); Empty
    }
  }
}
  

ОРИГИНАЛЬНОЕ СООБЩЕНИЕ

Я новичок в scala, поэтому, возможно, я делаю здесь что-то неправильно, но у меня есть выражение сопоставления с шаблоном, которое, похоже, пропущено. Вот код:

 lazy val dispatch: LiftRules.DispatchPF = {
   // Explicitly setting guard to false to trigger the scenario
   case req: Req if false => () => Full(...)
   // This should match since previous case will never match
   case Req("api" :: "test" :: Nil, suffix, GetRequest) => () => Full(...)
   // This is actually called...
   case _ => () => println("not sure what's going on"); Empty
}
  

Если я удалю первое case выражение, все будет работать так, как ожидалось. Я склонен думать, что это ошибка (https://issues.scala-lang.org/browse/SI-2337 ), но кто-нибудь знает об обходном пути?

Комментарии:

1. Как вы думаете, почему второй случай должен совпадать? Что вы вводите в качестве аргумента?

2. В этом случае вам нужно уменьшить «ошибку» до воспроизводимого сеанса REPL. Например, определите свои типы / классы, входные данные и т.д. Кроме того, в операторе промежуточного варианта выведите значение отправляемого объекта!

3. Если все работает, как ожидалось, путем исключения первого случая, то это было бы обходным решением, не так ли? Зачем вам вообще нужен первый случай, поскольку без if false он соответствует каждому Req , так что вы никогда не доберетесь до второго случая? И чтобы повторить очень очевидный момент из предыдущих двух комментариев, второй случай будет соответствовать только очень определенному элементу, но вы не предоставили никаких доказательств или проверки того, что соответствие действительно выполняется для такого элемента. И это имеет мало или вообще не имеет сходства с SI-2337, где первый случай является экстрактором (который req:Req им не является).

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

5. Можете ли вы поместить код тестового примера, который работает в REPL и выдает неправильное соответствие. Слишком много вещей упущено. "api" :: "test" :: Nil, suffix, GetRequest может быть проблема. Вы также где-то match пропускаете?

Ответ №1:

По крайней мере, измените последнюю строку:

 case other => () => { println("not sure what's going on "   other); Empty }
  

и скажите нам, что он печатает

Комментарии:

1. Я думаю, потому что это не было ответом; Я добавил это только как ответ для форматирования и для того, чтобы OP мог изменить свой вопрос, и тогда я мог бы предположительно изменить свой ответ на его основе. Я согласен; резкое отклонение

2. Лично мне не нравится «особенность» имен в нижнем регистре Scala в match . Я бы предпочел написать case a:Any => // do something with a .

3. @Landei, этот ответ скорее комментарий, чем «ответ» (не отрицательный ответ).

Ответ №2:

Я только что ввел пример, который, похоже, соответствует тому же сценарию, что и у вас в коде, и он работает так, как ожидалось в Scala 2.9:

 case class Foo(x:String)
val bar = Foo("bar")
bar match {
    case x:Foo if false => println("Impossible")
    case Foo(x) => println("Expected: "   x)
    case _ => println("Should not happen")
}
  

Который выводит Expected: bar

Посмотрите, сможете ли вы воспроизвести ошибку в отдельном примере, подобном этому, так что, возможно, мы (или, если это ошибка, команда разработчиков Scala, сможем выяснить, что происходит не так 🙂

Примечание: Похоже, я неправильно истолковал ваш вопрос в первый раз, извините за это. Я все равно не собираюсь удалять эту часть, потому что это может быть полезно кому-то еще.

При использовании сопоставления с шаблоном будет выполнена первая соответствующая инструкция case, и после этого совпадение будет завершено, а все остальные инструкции case будут проигнорированы!

Ваша проблема здесь в том, что первое утверждение

 case req:Req =>
  

соответствует каждому экземпляру Req . После сопоставления первого оператора и выполнения его кода Scala просто выпрыгивает из выражения сопоставления, потому что оно завершено. Второй оператор case бы соответствовал, но он никогда не выполняется ни для одного данного экземпляра Req , потому что совпадает первый. Насколько я помню, это известно как shadowing оператор case.

Поэтому переместите свой второй оператор case перед первым, и все должно быть в порядке.

Обратите внимание, что именно поэтому при сопоставлении с шаблоном более конкретные случаи соответствия должны быть на первом месте, а более общие операторы case должны идти последними.

Комментарии:

1. Я подозреваю, что вы не прочитали это неправильно… по крайней мере, это проблема с упорядочением … но трудно сказать, поскольку OP сделал так много предположений и пропустил так много важной информации.

2. Ответ содержит пару неточностей: в вопросе не было безусловного случая, соответствующего всем Req , за исключением случая, поэтому здесь нет проблемы с упорядочением; обновленная редакция — это скорее комментарий (например, «Посмотрите, можете ли вы воспроизвести ошибку в автономном примере, подобном этому»), чем фактический ответ. Я смог воспроизвести проблему в своем отзыве. (не понижающий параметр).

3. Да, я в примечании, что, по-моему, неправильно истолковал вопрос и что я не хотел его удалять, потому что это могло бы быть полезно для кого-то другого. Затем я попытался привести автономный пример того, что, как я думал, было воспроизведением примера, приведенного создателем вопроса, что я просто сделал в ответе (вместо комментария, который также был бы плохо отформатирован), чтобы помочь создателю вопроса изменить его для воссоздания проблемы. Я действительно не понимаю, почему мои усилия заслуживают отрицательного голосования, потому что это не могло быть бесполезным, имо.

Ответ №3:

Это действительно ошибка, на которую вы ссылаетесь в Scala bug tracker. Req это класс без регистра с сопутствующими методами извлечения, поэтому ошибка проявляется здесь. Введенное вами обходное решение кажется прекрасным.

Для тех, кому интересно, вот пример случая, в котором проявляется ошибка:

 class Sample(val a: String)

object Sample {
    def apply(a: String) = new Sample(a)
    def unapply(s: Sample) = Option(s.a)
}

val s = new Sample("a")

val r = s match {
    case n: Sample if false => "Wrong 1: "   n
    case Sample(_) => "Yay" 
    case n => "Wrong 2: "   n
}

println("Found "   r)
assert(r == "Yay")
  

Комментарии:

1. В вашем случае работает следующее: case s:Sample => "Yay" // s.a to access a