Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this pattern matching work as expected in Scala?

I'm trying to replicate the powerful pattern matching example that Joshua Suereth presented in his Devoxx 2013 talk titled "How to wield Scala in the trenches". Unfortunately I cannot achieve what he described and I cannot understand what is wrong. Can someone give me a hint at what I'm missing? (My Scala version is 2.10.3)

Please see the self contained code below:

case class Person(name: String, residence: Seq[Residence])
case class Residence(city: String, country: String)

object LivesIn {
  def unapply(p: Person): Option[Seq[String]] =
    Some(
      for(r <- p.residence)
      yield r.city
    )
}

class StringSeqContains(value: String) {
  def unapply(in: Seq[String]): Boolean =
    in contains value
}

object PatternPower extends App {

  val people =
    Seq(Person("Emre", Seq(Residence("Antwerp", "BE"))),
      Person("Ergin", Seq(Residence("Istanbul", "TR"))))

  val Istanbul = new StringSeqContains("Istanbul")

  // #1 does not work as expected, WHY?
  println(
    people collect {
      case person @ LivesIn(Istanbul) => person
    }
  )

  // #2 works as expected
  println(
    people collect {
      case person @ LivesIn(cities) if cities.contains("Istanbul") => person
    }
  )

  // #3 works as expected
  println(
    people collect {
      case person @ Person(_, res) if res.contains(Residence("Istanbul", "TR")) => person
    }
  )

}

When I compile and run it I get:

List()
List(Person(Ergin,List(Residence(Istanbul,TR))))
List(Person(Ergin,List(Residence(Istanbul,TR))))

As denoted in the source code, I fail to grasp why the first pattern does not produce the same result as the remaining two pattern matches. Any ideas why?

like image 465
Emre Sevinç Avatar asked Mar 21 '23 03:03

Emre Sevinç


2 Answers

Your LivesIn extractor requires a Seq for an argument.

The following variation does what you expect:

println(
  people collect {
    case person @ LivesIn(List("Istanbul")) => person
  }
)
like image 152
Randall Schulz Avatar answered Apr 01 '23 08:04

Randall Schulz


After some thinking and Googling, I realized that one should add () to the inner extractor (thanks to The Neophyte's Guide to Scala Part 1: Extractors).

In other words, the following works as expected:

people collect {
  case person @ LivesIn(Istanbul()) => person
}

whereas the following code silently, without any complaints, returns List():

people collect {
  case person @ LivesIn(Istanbul) => person
}

Unless I'm mistaken in another way (e.g. there is way to make it work without parantheses), I think technical presenters should be more careful with the code snippets / pseudo-code snippets (so that some of the curious audience will not lose sleepless hours ;-)

like image 23
Emre Sevinç Avatar answered Apr 01 '23 07:04

Emre Sevinç