Scala map of function using "_" - scala

Why is it that (in the Scala REPL) I can write, for example,
def double(d: Int) = 2*d
(0 until 10).zipWithIndex.map(i => double(i._1))
or just
(0 until 10).zipWithIndex.map(_._1)
yet I can't write
(0 until 10).zipWithIndex.map(double(_._1))
error: missing parameter type for expanded function ((x$1) => x$1._1) (0 until 10).zipWithIndex.map(double(_._1))
?

Scala tries to expand _._1 inside double. So, it thinks you want to have
(0 until 10).zipWithIndex.map(double(i => i._1))
However, it also sees that i => i._1 does not really fit into one of double’s argument types, so it complains and asks you to give a type hint to help the compiler. In this case, though, there cannot be a correct type definition, so the error message is kind of wrong there.

Related

Folding lists in scala

Folding list in scala using /: and :\ operator
I tried to to look at different sites and they only talk about foldRight and foldLeft functions.
def sum(xs: List[Int]): Int = (0 /: xs) (_ + _)
sum(List(1,2,3))
res0: 6
The code segment works as described. But I am not able to completely understand the method definition. What I understand is that the one inside the first parenthesis -> 0 /: xs where /: is a right associate operator. The object is xs and the parameter is 0. I am not sure about the return type of the operation (most probably it would be another list?). The second part is a functional piece which sums its two parameters. But I don't understand what object invokes it ? and the name of function. Can someone please help me to understand.
The signature of :/ is
/:[B](z: B)(op: (B, A) ⇒ B): B
It is a method with multiple argument lists, so when it is just invoked with on argument (i.e. 0 /: xs in your case) the return type is (op: (B, A) ⇒ B): B. So you have to pass it a method with 2 parameters ( _ + _ ) that is used to combine the elements of the list starting from z.
This method is usually called foldLeft:
(0 /: xs)(_ + _) is the same as xs.foldLeft(0)(_ + _)
You can find more details here: https://www.scala-lang.org/api/2.12.3/scala/collection/immutable/List.html
Thanks #HaraldGliebe & #LuisMiguelMejíaSuárez for your great responses. I am enlightened now!. I am just summarisig the answer here which may benefit others who read this thread.
"/:" is actually the name of the function which is defined inside the List class. The signature of the function is: /:[B](z: B)(op: (B, A) ⇒ B): B --> where B is the type parameter, z is the first parameter; op is the second parameter which is of functional type.
The function follows curried version --> which means we can pass less number of parameters than that of the actual number. If we do that,
the partially applied function is stored in a temporary variable; we can then use the temporary variable to pass the remaining parameters.
If supplied with all parameters, "/:" can be called as: x./:(0)(_+_) where x is val/var of List type. OR "/:" can be called in two steps which are given as:
step:1 val temp = x./:(0)(_) where we pass only the first parameter. This results in a partially applied function which is stored in the temp variable.
step:2 temp(_+_) here using the partially applied function temp is passed with the second (final) parameter.
If we decide to follow the first style ( x./:(0)(_+_) ), calling the first parameter can be written in operator notion which is: x /: 0
Since the method name ends with a colon, the object will be pulled from right side. So x /: 0 is invalid and it has to be written as 0 /: x which is correct.
This one is equivalent to the temp variable. On following 0 /: x, second parameter also needs to be passed. So the whole construct becomes: (0/:x)(_+_)
This is how the definition of the function sum in the question, is interpreted.
We have to note that when we use curried version of the function in operator notion, we have to supply all the parameters in a single go.
That is: (0 /: x) (_) OR (0 /: x) _ seems throwing syntax errors.

What does an underscore after a scala method call mean?

The scala documentation has a code example that includes the following line:
val numberFunc = numbers.foldLeft(List[Int]())_
What does the underscore after the method call mean?
It's a partially applied function. You only provide the first parameter to foldLeft (the initial value), but you don't provide the second one; you postpone it for later. In the docs you linked they do it in the next line, where they define squares:
val numberFunc = numbers.foldLeft(List[Int]())_
val squares = numberFunc((xs, x) => xs:+ x*x)
See that (xs, x) => xs:+ x*x, that's the missing second parameter which you omitted while defining numberFunc. If you had provided it right away, then numberFunc would not be a function - it would be the computed value.
So basically the whole thing can also be written as a one-liner in the curried form:
val squares = numbers.foldLeft(List[Int]())((xs, x) => xs:+ x*x)
However, if you want to be able to reuse foldLeft over and over again, having the same collection and initial value, but providing a different function every time, then it's very convinient to define a separate numbersFunc (as they did in the docs) and reuse it with different functions, e.g.:
val squares = numberFunc((xs, x) => xs:+ x*x)
val cubes = numberFunc((xs, x) => xs:+ x*x*x)
...
Note that the compiler error message is pretty straightforward in case you forget the underscore:
Error: missing argument list for method foldLeft in trait
LinearSeqOptimized Unapplied methods are only converted to functions
when a function type is expected. You can make this conversion
explicit by writing foldLeft _ or foldLeft(_)(_) instead of
foldLeft. val numberFunc = numbers.foldLeft(ListInt)
EDIT: Haha I just realized that they did the exact same thing with cubes in the documentation.
I don't know if it helps but I prefer this syntax
val numberFunc = numbers.foldLeft(List[Int]())(_)
then numberFunc is basically a delegate corresponding to an instance method (instance being numbers) waiting for a parameter. Which later comes to be a lambda expression in the scala documentation example

Behaviour of Scala underscore not work like element of list

Hi I am new in scala, and of course have a newbie question,
why the underscore character not work in this cases
my undertanding about underscore in this case it represent one element of the list
val listOftuple = List(("cat","tom"),("mouse","jerry"),("dog","spike"))
val listOfarray = List(Array("cat","tom"),Array("mouse","jerry"),Array("dog","spike"))
tuplelist.map(it=>it._1+it._2) //Ok
tuplelist.map(_._1+_._2) //Not work, give me: error: missing parameter type for expanded function
listOfarray.map(_.length) //Here work, like array.length
listOfarray.map(it=>it(0)+it(1)) //Ok
listOfarray.map(_=>_(0)+_(1))//Not work, give me: error: missing parameter type for expanded function
listOfarray.map(_(0)+_(1))//Not work, give me: error: missing parameter type for expanded function
may be i fall in a lexical misunderstanding ?
how to fix the error msg?
greetings
tuplelist.map(_._1+_._2)
Each time you use _, it stands for another parameter. That is, _ + _ is equivalent to (x,y) => x+y, not x => x+x. So the above is equivalent to tuplelist.map((x, y) => _._1 + _._2), which does not work because map expects a function that takes only one argument.
listOfarray.map(_=>_(0)+_(1))
Using _ as the name of a function parameter just means that the parameter will be ignored. When you use _ inside the function's body, it is still the function shortcut operator, not a reference to the ignored parameter. So the above lambda is equivalent to ignoredParam => (x, y) => x + y.
listOfarray.map(_(0)+_(1))
(x,y) => x(0) + y(1), but you want x => x(0) + x(1).
how to fix the error msg?
In all of these cases, using an explicit parameter list is the correct solution. You simply can't use the shortcut notation if you want to use the same parameter more than once.

why scala lambda with _ can't using && to combine two bool expression

As far as I understand .
_ is a short lambda to omit a=>
i find this code (can find here scala-function-true-power)
val file = List("warn 2013 msg", "warn 2012 msg", "error 2013 msg", "warn 2013 msg")
val size = file.filter(_.contains("warn")).filter(_.contains("2013")).size
//val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
val size2 = file.filter( a=> a.contains("warn") && a.contains("2013")).size
println("cat file | grep 'warn' | grep '2013' | wc : " +size )
the line to get size1 has syntax error,looks like it can't recognize the "_" ,it's not a element in fileList.
but i use a=>,the normal kind,it works good .
so,why the scala work by this way?
is there more difference in _ and a=> ?
In scala, any _ placeholder is matched against the passed arguments in the context of calling function. So for example if the signature of the function you are trying to use is f : A ⇒ B and you are calling something like collectionOfFunctA.map(_.f) - Scala compiler will infer the correct type of the function and will use the first underscore to put the actual item from a collection and call the function f over it. But if you will try to write it as collectionOfFunctA.map(_.f + _.size) - that will fail, because Scala compiler will pick up the first placeholder as of type that has function f defined, and the second underscore will not match any function in the context. So it will expect to have a function that takes two parameters instead of one.
More on this
As jdevelop says, but here in the words of the compiler/REPL:
scala> val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
<console>:8: error: missing parameter type for expanded function ((x$1, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
^
<console>:8: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
val size1 = file.filter(_.contains("warn") && _.contains("2013")).size
^
You see that hint: for expanded function ((x$1, x$2) => x$1.contains("warn").$amp$amp(x$2.contains("2013")))
It is expecting 2 parameters while there is just one.
You can think of the place holder as being matched with the lambda's arguments positionnally.
The first occurrence of the _ is matched with the first argument, the second occurence is matched with the second argument, etc.
As the other answers have shown, this means that using the placeholder twice will be desugared as trying to pass a lamba with 2 arguments to the filter which only expects one.
In your example :
val size = file.filter(_.contains("warn") && _.contains("2013")).size
would be desugared as
val size = file.filter((a,b)=>a.contains("warn") && b.contains("2013")).size
which will not compile since filter expects a predicate p: A => Boolean
Now, a reason the placeholder is matched positionnally is to avoid ambiguity in lambdas with more than one argument.
How can the compiler guess the correct implementation for the following case if the place holder can be reused multiple times for the same argument:
file.fold("")(_++_)
Should it be desugared as :
file.fold("")((a,b)=> a++b )
or as
file.fold("")((a,b)=> a++a )
or as
file.fold("")((a,b)=> b++b )
and worse, what would you expect for
file.fold("")(_++_++_)
There is no general way for the compiler to infer the correct implementation.
One might argue for relaxing the constraint when the expected lambda only accepts one argument. I suggest doing a more detailed research before taking the first steps to the scala improvement process as it seems likely that this particular design decision has been challenged and explained before.
If you are worried about the performance of iterating over the list twice (which is the case when you write)
file.filter(_.contains("warn")).filter(_.contains("2013")).size
In theory it should be possible for the compiler to detect that both filters can be applied within the same iteration.
In scala, the collections are eager by default but you can get the lazy evaluation by using views.
The current implementation has known issues which are being worked on. Other collection implementations in scala are actively being developed to be able to combine transformations and computations by default (see psp-std for example)

Why Scala compiler throws IndexOutOfBoundException while applying foreach on a mutable list

I have the following Code.
import scala.collection.mutable.MutableList
val x = MutableList[Int]()
(1 to 10).foreach(x+=1)
I get the java.lang.IndexOutOfBoundsException: 1 error.
but,
(1 to 10).foreach(println) this does not throw any error.
the indexOutOfBoundException can be solved by using lambda Operator as follows:
(1 to 10).foreach(_ => x+=1)
Everything works fine with this.
My questions are :
1. Why do i need to use lambda operator in first case unlike second one?
2. Why the compiler throws IndexOutOfBoundException, i suppose this is not the correct context for this Exception.
What happens is a few little conveniences in the library conspiring to bite you.
foreach signature is
def foreach(f: A => Unit): Unit
In your case, A is Int, so it needs a function which takes an Int and returns Unit. Fine with println. Note you have just written println, not println(something), which would not have been a function.
One would expect x += 1 to be just an instruction, so it would have type Unit, not a function, not a valid argument of foreach, and one would get a helpful compile-time error. But += in MutableList actually returns the list, which is convenient as it makes chaining operations easier:
def +=(elem: A): this.type
So the type of x+= 1 is MutableList[Int]. One will still expect a compilation error, not a function. Except that MutableLists (all Seqs actually) are functions, with Int parameters, returning the type of the Seq's elements. The function simply returns the i-th element. Again, that may be convenient, you may simply pass the seq where a function is expected, instead of having to write i => seq(i). Now you have a function, with an Int parameter, what foreach expects.
Still, it does not returns Unit, but Int. However, scala will accept that as an Int => Unit, just discarding the value. So it compiles.
Now to what it does: first, it evaluates the argument in foreach, so it calls x+=1, getting the list x, which now contains an element. It will then call this function, which is the access to the i-th element with arguments ranging from 1 to 10). Doing that, it would not add values to the list, but just access elements at the given indexes. It then fails immediately, as the list contains just one element, at index 0, so calling with 1 throws the IndexOutOfBoundException.