Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why scala's pattern maching does not work in for loops for type matching?

I'm coding against an API that gives me access to remote file system. The API returns a list of files and directories as list of node objects (parent to file and directory).

I want to work only on a directories, ignoring files. I've tried to use type pattern matching in for loop but it does not work:

for {
    dir: CSDir <- workarea.getChildren() // <-- I'm getting an error here complaining about type conversion
} {
    println(dir)
}

Here is a similar example using scala basic objects to run it without dependencies:

val listOfBaseObjects:List[Any] = List[Any]("a string", 1:Integer);

for (x: String <- listOfObjects) {
  println(x)
}

I end up using a regular pattern matching in side of for loop and that works fine:

// This works fien
for (child <- workarea.getChildren()) {
  child match {
    case dir: CSDir => println(dir)
    case _ => println("do not nothing")
  }
}

Question:

Can you tell me why the first /second example does not work in scala 1.9?

In the "Programming in Scala" the for loop is advertised to use the same pattern matching as the match so it should work.

If the for and match are different it would be great if you could point me to some articles with more details. What about pattern matching in assignment?

Update:

I can't accept an answer that states that it is impossible to skip elements in for loop as this contradicts with the "Prog. in scala". Here is a fragment from section 23.1:

pat <- expr ... The pattern pat gets matched one-by-one against all elements of that list. ... if the match fails, no MatchError is thrown. Instead, the element is simply discarded from the iteration

and indeed the following example works just fine:

scala> val list = List( (1,2), 1, 3, (3,4))
scala> for ((x,y) <- list) { println (x +","+ y) }
1,2
3,4

Why then type matching does not work?

like image 531
Piotr Czapla Avatar asked Jul 09 '12 11:07

Piotr Czapla


People also ask

Does Scala have pattern matching?

Pattern matching is a way of checking the given sequence of tokens for the presence of the specific pattern. It is the most widely used feature in Scala. It is a technique for checking a value against a pattern. It is similar to the switch statement of Java and C.

How does Scala pattern matching work?

Pattern matching is a mechanism for checking a value against a pattern. A successful match can also deconstruct a value into its constituent parts. It is a more powerful version of the switch statement in Java and it can likewise be used in place of a series of if/else statements.

What are the different ways to implement match expressions in scala?

Using if expressions in case statements First, another example of how to match ranges of numbers: i match { case a if 0 to 9 contains a => println("0-9 range: " + a) case b if 10 to 19 contains b => println("10-19 range: " + b) case c if 20 to 29 contains c => println("20-29 range: " + c) case _ => println("Hmmm...") }

Which method of case class allows using objects in pattern matching?

Case classes are Scala's way to allow pattern matching on objects without requiring a large amount of boilerplate. In the common case, all you need to do is add a single case keyword to each class that you want to be pattern matchable.


2 Answers

This is the long-standing issue 900 and has been discussed many times before. The common workaround is to use something like:

for (y@(_y:String) <- listOfBaseObjects) {
    println(y)
}

A nicer version is provided by Jason Zaugg in the comments to the above-mentioned ticket:

object Typed { def unapply[A](a: A) = Some(a) }

for (Typed(y : String) <- listOfBaseObjects) {
    println(y)
}
like image 123
Niklas B. Avatar answered Nov 16 '22 02:11

Niklas B.


What you want to do is essentially: iterate over all elements of workarea.getChildren() that are of type CSDir (in other words: matching some criteria). Ordinary loop/for comprehension iterates over all elements. You cannot say: iterate over all elements having this type and skip others. You must be more explicit.

What do you think about:

workarea.getChildren() collect {case dir: CSDir => dir} foreach println

It does exactly what you want: collect all elements of workarea.getChildren() and for each of them call println.

like image 26
Tomasz Nurkiewicz Avatar answered Nov 16 '22 01:11

Tomasz Nurkiewicz