Por que a correspondência de padrões no Scala não funciona com variáveis?

113

Execute a seguinte função:

def fMatch(s: String) = {
    s match {
        case "a" => println("It was a")
        case _ => println("It was something else")
    }
}

Este padrão combina perfeitamente:

scala> fMatch("a")
It was a

scala> fMatch("b")
It was something else

O que eu gostaria de poder fazer é o seguinte:

def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case target => println("It was" + target)
        case _ => println("It was something else")
        }
}

Isso dá o seguinte erro:

fMatch: (s: String)Unit
<console>:12: error: unreachable code
               case _ => println("It was something else")

Acho que é porque ele pensa que o destino é na verdade um nome que você gostaria de atribuir a qualquer que seja a entrada. Duas questões:

  1. Por que esse comportamento? Não é possível apenas procurar variáveis ​​existentes no escopo que tenham o tipo apropriado e usá-las primeiro e, se nenhuma for encontrada, tratar o alvo como um nome para o padrão de correspondência?

  2. Existe uma solução alternativa para isso? Alguma maneira de combinar padrões com variáveis? No final das contas, pode-se usar uma instrução if grande, mas a caixa de correspondência é mais elegante.

Henry Henrinson
fonte
1
Eu acredito que esta pergunta, código e respostas estão desatualizados no Scala 2.12.x. Seria bom se a versão à qual se aplica fosse mencionada como parte da pergunta.
conny

Respostas:

217

O que você está procurando é um identificador estável . No Scala, eles devem começar com uma letra maiúscula ou estar entre crases.

Ambos seriam soluções para o seu problema:

def mMatch(s: String) = {
    val target: String = "a"
    s match {
        case `target` => println("It was" + target)
        case _ => println("It was something else")
    }
}

def mMatch2(s: String) = {
    val Target: String = "a"
    s match {
        case Target => println("It was" + Target)
        case _ => println("It was something else")
    }
}

Para evitar a referência acidental a variáveis ​​que já existiam no escopo delimitador, acho que faz sentido que o comportamento padrão seja que os padrões em minúsculas sejam variáveis ​​e não identificadores estáveis. Somente quando você vê algo começando com maiúsculas, ou em marcações anteriores, você precisa estar ciente de que vem do escopo circundante.

Ben James
fonte
3
Aposto que isso vem de Erlang, onde as variáveis ​​começam com uma letra maiúscula e os símbolos com uma minúscula.
Emil Ivanov
11
Observe que targeté um valor ( val) e não uma variável ( var). Não funciona com variáveis.
Luigi Plinge de
Maiúsculas? Tons de FORTRAN. Fraco, Martin, fraco.
Malvolio
13
@Emil Na verdade, identificadores em maiúsculas em Scala denotam constantes. Portanto, uma correspondência de padrão em um identificador em maiúsculas significa a comparação com uma constante. Isso ajuda seriamente com coisas como Nil, que eu aposto que é o verdadeiro motivo.
Daniel C. Sobral de
Parece que não se pode usar thiscomo um identificador estável para combinar padrões contra ele, a única maneira parece ser usar uma proteção de igualdade como case x if x == this =>. Provavelmente uma limitação sintática, caso contrário, deve funcionar semanticamente pelo menos dentro de objects.
Nader Ghanbari,