4

I am trying to filter a map from strings to ints in scala and I am running into a weird issue.

If I put the following into REPL:

scala> val myMap = Map("a" -> 1, "b" -> 2, "c" -> 3)
myMap: scala.collection.immutable.Map[java.lang.String,Int] = 
| Map(a -> 1, b -> 2, c -> 3)

It's ok so far, and this works...

scala> myMap.filter(_._2 > 1)
res9: scala.collection.immutable.Map[java.lang.String,Int] = Map(b -> 2, c -> 3)

but this fails...

scala> myMap.filter((k:java.lang.String, v:Int) => v > 1)
<console>:9: error: type mismatch;
 found   : (java.lang.String, Int) => Boolean
 required: ((java.lang.String, Int)) => Boolean
   myMap.filter((k:java.lang.String, v:Int) => v > 1)

My question is what is going on with the error message and the extra pair of parentheses? If I try putting in an extra set of parentheses I get error: not a legal formal parameter.

chuck taylor
  • 2,476
  • 5
  • 29
  • 46

2 Answers2

16

myMap.filter expects a function of type Tuple2[String, Int] => Boolean, which is equivalent to ((String, Int)) => Boolean. You're passing it a function of type (String, Int) => Boolean; that is, a function that takes two arguments rather than a single Tuple2.

Two ways to get it to work:

 myMap.filter { case (k, v) => v > 1 }

and

 myMap.filter(Function.tupled((k, v) => v > 1))

The first works by pattern matching, and the second by converting the function (String, Int) => Boolean to ((String, Int)) => Boolean.

By the way, there has been discussion about unifying tuples and function argument lists. Maybe in a future version of Scala, all functions will take a single parameter (which might be a tuple).

Kipton Barros
  • 21,002
  • 4
  • 67
  • 80
  • Agreed. Function.tupled is explicit while the pattern match seems more like a trick. – andyczerwonka Aug 22 '11 at 23:28
  • 1
    Sorry if this is a dumb question, but why does the pattern matching with case work with curly braces {} but not parentheses? – chuck taylor Aug 24 '11 at 00:18
  • Not a dumb question at all. I don't know a better answer than "that's what the Scala Language Spec says". You might be interested in this discussion of the difference between `()` and `{}`: http://stackoverflow.com/questions/4386127/what-is-the-formal-difference-in-scala-between-braces-and-parentheses-and-when-s – Kipton Barros Aug 24 '11 at 03:15
  • @Kipton Is Function.tupled deprecated? It had been deprecated, but that had been reversed because Function.tupled is friendlier to type inference. – Daniel C. Sobral Aug 24 '11 at 18:36
  • @Daniel, it's deprecated according to the source code linked from these nightly docs: http://www.scala-lang.org/archives/downloads/distrib/files/nightly/docs/library/index.html#scala.Function$ – Kipton Barros Aug 24 '11 at 23:19
  • @Kipton Actually, it is commented. Not `/** */`-scaladoc commented, but plain `//` commented. – Daniel C. Sobral Aug 25 '11 at 12:18
  • @Daniel Thanks for catching that, the `//` threw me off. So apparently `Function.tupled` is **not** deprecated. – Kipton Barros Aug 25 '11 at 19:23
6

filter accepts a function that accepts only one parameter. In your expression, the expression accepts two parameters. However, the element happen to be a pair, so you may think you can give two parameters. The correct way of putting it would be something like:

myMap.filter (p => p._2 > 1)

That is, I receive a pair, p, and its second element must be greater than 1.

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87