I'm taking my first interesting steps (non-hello-world level) with Scala (2.9.1) and I'm stuck trying to understand a very uninformative error message.
It goes much like this:
error: type mismatch;
found : (Int, Array[InputEntry]) => (Int, Double)
required: (Int, Array[InputEntry]) => ?
entries.groupBy(grouper).map((k: Int, ies: Array[InputEntry]) => (k, doMyStuff(ies)))
As you can guess process in this snippet is supposed to be where some processing goes on, and it's actually a well defined function with signature Array[InputEntry] => Double.
Grouper's signature, instead, is Array[InputEntry] => Int.
I've tried to extract a function and replace the lambda but it was useless, and I'm stuck trying to understand the question mark in the error...
Any ideas?
Edit: I should clarify that InputEntry is a class I've defined, but for the sake of this example it seems to me like it's hardly relevant.
This looks like the problem:
.map((k: Int, ies: Array[InputEntry]) => (k, doMyStuff(ies)))
You need to use a case statement to unapply the params and assign them to local variables. You also need to use {} instead of () because it is an anonymous function now.
entries.groupBy(grouper).map{case (k, ies) => (k, doMyStuff(ies))}
Here's a more simple example.
scala> val x = List(("a",1),("b",2))
x: List[(java.lang.String, Int)] = List((a,1), (b,2))
scala> x.map{ case (str, num) => num }
res5: List[Int] = List(1, 2)
If you don't want to use a case statement, you have to keep the tuple as a single variable.
scala> x.map(tuple => tuple._2)
res6: List[Int] = List(1, 2)
Related
I'm trying to sort a list by how close the entries of a list are to num.
I decided to try use sortWith, but the following snippet:
list.sortWith(math.abs(_ - num) < math.abs(_ - num))
failed with missing parameter type for _ in scala.
list is of type List[Int].
Following the other threads, I know that _ is somehow type ambiguous, but I'm not sure why, (and why the following snippet is not type ambiguous):
scala> val sortedDudes = dudes.sortWith(_.name < _.name)
sortedDudes: Array[Person] = Array(Al, Paul, Tyler)
(Source)
def foo = {
val num = 2
val list: List[Int] = List(1, 2)
list.sortWith((a, b) => math.abs(a - num) < math.abs(b - num))
}
work perfectly. It's because scala trying get _ from math.abs, not sortWith
In Scala, _ can be used in a variety of different situations to mean different things. The answers on this question should help clarify a few of them.
Going back to the question, it seems the OP is trying to use _ for parameter replacement. Consider the following example
List(1,2,5,7).filter(_ > 4)
Here filter takes a function of type A => Unit, so the above is shorthand for
List(1,2,5,7).filter(x => x > 4)
The underscore can stand for more than one parameter, but it must be used to refer to each parameter exactly once. This is why the sortedDudes snippet in the OP works. Therefore the following is legal.
List(1,2,5,7).reduce(_ + _)
which is shorthand for
List(1,2,5,7).reduce((a,b) => a + b)
I think the problem with the original snippet is that the compiler cannot unambiguously parse it into something of type (A, A) => Boolean as required by the sortWith method. We can give the compiler a little help as follows.
scala> def op(int: Int, num: Int) = math.abs(int - num)
op: (int: Int, num: Int)Int
scala> List(1,7,5,10).sortWith(op(_, 5) < op(_, 5))
res0: List[Int] = List(5, 7, 1, 10)
I'm currently learning Scala, and I just wondered at fold-left.
Since fold-left is curried, you should be able to get a partially applied function(PAF) with a first parameter as below.
(0 /: List(1, 2, 3)) _
But actually, I've got an error.
<console>:8: error: missing arguments for method /: in trait TraversableOnce;
follow this method with `_' if you want to treat it as a partially applied function
Then I tried same thing by fold-right such as below
(List(1, 2, 3) :\ 0) _
In this way, it went correctly, and I could get a PAF such as ((Int, Int) => Int) => Int
I know I can get a PAF by using foldLeft method, but I wonder whether it is possible to express it with '/:' or not.
The underscore syntax does not work well with right-associative methods that take multiple parameter lists. Here are the options I see:
Declare a variable type:
val x: ((Int, Int) => Int) => Int = 0 /: List(1, 2, 3)
Similarly, use type ascription:
val x = (0 /: List(1,2,3)) : ((Int, Int) => Int) => Int
Use the postfix notation:
val x = List(1,2,3)./:(0) _
Use the foldLeft synonym:
val x = List(1,2,3).foldLeft(0) _
I played around with it, and couldn't find a configuration that works.
There's always the more explicit:
val f = List(1,2,3,4,5).foldLeft(0)_
Which is arguably neater. I'll keep poking around though.
Edit:
There's this:
val f2 = (0 /: List(1,2,3,4,5))(_: (Int,Int) => Int)
val x = f2(_+_)
But that's getting pretty ugly. Without the type annotation, it complains. That's the best I could do though.
Suppose this function
def func[A](data: List[A], mapper: A => String) = {
data.map(item => mapper(item))
}
Why this code doesn't compile:
val list = List(1, 2, 3)
func(list, a => a.toString)
But this one does:
val list = List(1, 2, 3)
func[Int](list, a => a.toString)
Or
val list = List(1, 2, 3)
func(list, (a: Int) => a.toString)
While a type can be inferred from list which is List of Int. Why doesn't scala infer the type here?
Is there any other way?
There is another way! It also happens to make for some nice syntatic sugar:
def func[A](data: List[A])(mapper: A => String) = data map mapper
which looks like:
func(myList){
case Left(one) => one
case Right(_) => default
}
The reason that you can not get the type information to flow the way you'd expect is that type information in Scala is left to right. In other systems, type information is known and deduced for useage where it is defined. You sometimes have to work around these limitations but at the same time, in this case, you can get to work with something that looks akin to your own defined control structure.
So...
func[Int]( //I've just told the typer what the information is and it can flow to the right.
func(list //the typer has to deduce the type and without a guide can not figure out what a => a.toString should be
This is also an old "issue" you can see here SI-4773.
Response to Q in Comment:
If you want to have a Seq[A => B] then I'd do something similar to
func[A, B](data: List[A])(actions: A => B*) = actions map {
data map
}
which is using varargs (translates to a WrappedArray, hence the map) to accept any list of commands so that you can pass is
func(list)(_.name, _.age, _.sex, _.stalker)
as far as pulling out and matching on what you've passed in:
func[A, B](data: List[A])(actions: (String, A => B)*) = actions map {
case (name, f) => (name, data map f)
}
wherein you're using the case statement to pattern match and extract the tuple.
I'm not sure why this doesn't work:
scala> case class Loader(n: String, x: String, l: List[String])
scala> val m: Map[String, (List[String])=>Loader] =
| Map("x" -> Loader("x", "x1", _:List[String]))
<console>:8: error: type mismatch;
found : (List[String]) => (java.lang.String, Loader)
required: (String, (List[String]) => Loader)
Map("x" -> Loader("x", "x1", _:List[String]))
but this does?
scala> Loader("t", "x", _:List[String])
res7: (List[String]) => Loader = function1>
scala> val m = Map("x" -> res7)
m: scala.collection.immutable.Map[java.lang.String,(List[String]) => Loader] =
Map((String,function1>))
One more victim of the overload of _ in Scala. Consider this:
f(_, 5) + 1 // Partial function application
f(_ + 1, 5) // Closure
In the first case, _ is replacing the entire parameter. In this case, it stands for a partial application of f. In practice, it's equivalent to x => f(x, 5) + 1, as the whole expression that contains f is turned into a closure.
In the second case, _ is part of an expression. In this case, the whole expression is turned into a closure, up to any expression delimiter -- by which I mean that if the expression is nested inside another, only the inner expression is turned into a closure. In practice, it is equivalent to f(x => x + 1, 5).
The parser was not sure where to put the beginning of the anonymous function. Sometimes you can solve this by adding another pair of parentheses (though not always):
val m: Map[String, (List[String])=>Loader] =
Map("x" -> (Loader("x", "x1", _:List[String])))
I don’t see any ambiguities here, so it might just not have been smart enough to figure it out. I think, the parser overlooked the possibility to have an anonymous function just after the -> (which also is a library construct and uses implicit magic and all the wicket stuff which makes the little parser’s mind loop).
When you write it as an explicit tuple, it’ll work fine.
val m: Map[String, (List[String])=>Loader] =
Map(("x", Loader("x", "x1", _:List[String])))
In the code snippet below - why do I have to give a type annotation for Nil?
Welcome to Scala version 2.8.0.RC2 (OpenJDK Server VM, Java 1.6.0_18).
Type in expressions to have them evaluated.
Type :help for more information.
scala> List(Some(1), Some(2), Some(3), None).foldLeft(Nil)((lst, o) => o match { case Some(i) => i::lst; case None => lst })
<console>:6: error: type mismatch;
found : List[Int]
required: object Nil
List(Some(1), Some(2), Some(3), None).foldLeft(Nil)((lst, o) => o match { case Some(i) => i::lst; case None => lst })
^
scala> List(Some(1), Some(2), Some(3), None).foldLeft(Nil:List[Int])((lst, o) => o match { case Some(i) => i::lst; case None => lst })
res1: List[Int] = List(3, 2, 1)
The problem is that Nil is an object that extends List. That means that Nil.type is a subclass of List and, therefore, the type for foldLeft's accumulator will be Nil.type.
This is one place I wish Scala tried a bit (or a lot, whatever it takes :) harder to get a better type inferred.
You can avoid the trouble by doing it like this
private def removeNone[A](xs:List[Option[A]]) = {
xs.filter(_.isInstanceOf[Some[_]]).map(_ match {
case Some(t) => t
case _ => ().asInstanceOf[A] //can't happen, needed to avoid warning
})
}
While that might not directly answer your question, I wrote this function a couple hours ago and thought it couldn't hurt sharing it. You can leave out the 2nd case if you don't mind getting a warning.
I also find the combination of map and filter easier to read than fold. I try to use fold only where necessary.