4

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?

2 Answers 2

6

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 } ) 
Sign up to request clarification or add additional context in comments.

5 Comments

Your example works, but it does not solve the mystery for me, because I'm trying to understand why LivesIn(Istanbul) does not work where Istanbul is defined as val Istanbul = new StringSeqContains("Istanbul").
The pattern target constraint you supplied is incompatible with the extractor.
Still trying to understand, e.g. the following code List(List("Istanbul")) collect {case city if Istanbul.unapply(city) => city} returns List(List(Istanbul)). I really fail to understand why the value Istanbul defined as new StringSeqContains("Istanbul") cannot be used as LivesIn(Istanbul) to achieve the expected result.
Look at the return type for the extractor (unapply) for LivesIn. That controls the "shape" (if you will) of the pattern constraining the match.
Unfortunately LivesIn(List("Istanbul")) does not work for a Person that has more than one Residence such as Person("Ergin", Seq(Residence("Istanbul", "TR"), Residence("Ankara", "TR")))). This is why I'm trying to understand how I can use an extractor such as StringSeqContains so that it can work for multiple residences.
1

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 ;-)

5 Comments

you might want to fill a bug report about this.
not sure if this is a bug, let me check with people on scala-user.
It's not a bug. Without the parentheses the match criteria is equality with the stable value that is the referent of specified name.
Not a bug indeed. Nevertheless, I think it is fair enough to categorize this as a pitfall.
Due to unsatisfactory answers in scala-users, I moved the question to the scala-internals list and got some solid answers.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.