Pourquoi la correspondance de modèles dans Scala ne fonctionne-t-elle pas avec des variables?

113

Prenez la fonction suivante:

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

Ce modèle correspond bien:

scala> fMatch("a")
It was a

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

Ce que j'aimerais pouvoir faire, c'est ce qui suit:

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

Cela donne l'erreur suivante:

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

Je suppose que c'est parce qu'il pense que la cible est en fait un nom que vous aimeriez attribuer à quelle que soit l'entrée. Deux questions:

  1. Pourquoi ce comportement? Vous ne pouvez pas simplement rechercher des variables existantes dans la portée qui ont un type approprié et les utiliser en premier et, si aucune n'est trouvée, traiter la cible comme un nom à mettre en correspondance?

  2. Y a-t-il une solution de contournement pour cela? Existe-t-il un moyen de faire correspondre les modèles aux variables? En fin de compte, on pourrait utiliser une grande instruction if, mais la casse de correspondance est plus élégante.

Henry Henrinson
la source
1
Je pense que cette question, ce code et ces réponses sont obsolètes depuis Scala 2.12.x. Ce serait bien si la version à laquelle s'applique était mentionnée dans le cadre de la question.
conny le

Réponses:

217

Ce que vous recherchez, c'est un identifiant stable . Dans Scala, ceux-ci doivent soit commencer par une lettre majuscule, soit être entourés par des backticks.

Ces deux solutions seraient des solutions à votre problème:

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")
    }
}

Pour éviter de faire accidentellement référence à des variables qui existaient déjà dans la portée englobante, je pense qu'il est logique que le comportement par défaut soit que les modèles en minuscules soient des variables et non des identificateurs stables. Ce n'est que lorsque vous voyez quelque chose commençant par des majuscules, ou entre des graduations arrière, que vous devez être conscient qu'il provient de la portée environnante.

Ben James
la source
3
Je parie que cela vient d'Erlang, où les variables commencent par une majuscule et les symboles avec une minuscule.
Emil Ivanov
11
Notez qu'il targets'agit d'une valeur ( val) et non d'une variable ( var). Cela ne fonctionne pas avec des variables.
Luigi Plinge
Majuscule? Nuances de FORTRAN. Faible, Martin, faible.
Malvolio
13
@Emil En fait, les identificateurs en majuscules dans Scala désignent des constantes. Ainsi, une correspondance de motif sur un identifiant majuscule est considérée comme une comparaison à une constante. Cela aide sérieusement avec des trucs comme Nil, dont je parie que c'est la vraie raison.
Daniel
On dirait que l'on ne peut pas utiliser thiscomme identifiant stable pour la correspondance de modèle, la seule façon semble être d'utiliser une protection d'égalité comme case x if x == this =>. Probablement une limitation syntaxique, sinon cela devrait fonctionner sémantiquement au moins dans objects.
Nader Ghanbari