Há alguma tempo atrás eu aprendi um pouco de Scala, uma linguagem muito interessante que roda sobre a JVM. Apesar de ser muito similar ao Java a primeira vista logo depois de um pouco mais de aprendizado lidamos com recursos muito diferentes do que temos na linguagem mais velha. Um recurso que me surpreendeu foi o casamento de padrões, ou pattern matching em inglês e vou usa-la de exemplo aqui pois até onde conheço o pattern matching do Scala é um dos mais completos. Diversas outras linguagens implementam também o recurso de maneira similar e as vezes com um pouco menos de poder, por isso vale conferir a documentação antes de prosseguir.

O casamento de padrões permite verificar se um dado objeto segue determinado padrão especificado pelo programador e muitas vezes ele é utilizado para desempacotar valores de um objeto e/ou verificar condições. Um exemplo pode ser visto a seguir em Scala:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
object CasamentoDePadroes extends App {
  var x: Any = "Desconhecido porém string";

  x match {
    case "oi" => println("Hello")
    case "Scala" => println("Hello Scala")
    case value: String => println(s"This is a string: $value")
    case _ => println("A strange object")
  }
}

Este é um dos exemplos mais comuns, parecido com o famigerado switch/case vemos que podemos testar valores e tipos com o casamento de padrões. Nas linhas 5 e 6 testamos o padrão contra valores, na linha 7 testamos contra um tipo (no caso String) e na linha 8 usamos uma expressão para capturar qualquer que seja o retorno.

O pattern matching do Scala também pode ser usado para desembrulhar valores de objetos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
object CasamentoDePadroes extends App {
  case class Pessoa(nome: String, idade: Int);

  var pessoa = Pessoa("Linus", 51);

  pessoa match {
    case Pessoa("Lucas", _) => println("Olá programador Lucas")
    case Pessoa(nome, idade) if idade > 50 => println(s"Olá Sr(a) $nome")
    case Pessoa(nome, _) => println(s"Olá $nome")
    case _: Pessoa => println("Olá desconhecido")
    case _ => println("Error")
  }
}

Neste exemplo brincamos muito mais com o casamento de padrões, além da estrutura similar ao switch/case também olhamos os valores internos do objeto em avaliação. Na linha 7 verificamos o nome da pessoa e caso seja “Lucas” exibimos uma mensagem personalizada, na linha 8 utilizamos uma funcionalidade chamada guarda, que é basicamente uma condição adicional ao pattern matching, assim se a pessoa tiver mais de 50 anos tratamos ela por um pronome personalizado. Verificamos casos menos específicos na linha 9 e 10 e caso chegue na linha 11 sabemos que temos um erro pois o objeto não é uma pessoa.

O pattern matching do Scala também atua como expressão, podendo retornar valores específicos se necessário (na verdade em Scala tudo é expressão):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
object CasamentoDePadroes extends App {
  case class Pessoa(nome: String, idade: Int);

  var pessoa = Pessoa("Linus", 51);

  var mensagem = pessoa match {
    case Pessoa("Lucas", _) => "Olá programador Lucas"
    case Pessoa(nome, idade) if idade > 50 => s"Olá Sr(a) $nome"
    case Pessoa(nome, _) => s"Olá $nome"
    case _: Pessoa => "Olá desconhecido"
    case _ => "Error"
  }
  
  println(mensagem)
}

Na verdade o pattern matching do Scala é usado em toda a linguagem é uma das bases para a programação funcional dela. O desembrulho de alguns parâmetros é permitido através justamente por causa do pattern matching:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
object CasamentoDePadroes extends App {
  // Pattern matching apicado a recuperação de valores de uma lista
  val lista = List(1,2,3,4,5)
  val primeiro :: resto = lista

  println(primeiro)
  println(resto)

  // Utilizando guardas como filtro
  val pares = for {
    x <- lista if x % 2 == 0
  } yield x

  print(pares)
}

Além destes exemplos o Scala permite muito mais coisas com seu casamento de padrões e outras linguagens como Rust, C#, Python (3.10), Java (17), etc oferecem esse recurso em algum nível. Vale apena consultar se sua linguagem possui este recurso, pois permite reduzir deixar o código muito mais organizado, principalmente aliado a orientação a objetos e tipagem.