In Scala, what is the difference between using the `_` and using a named identifier?

匆匆过客 提交于 2019-12-22 03:43:43

问题


Why do i get an error when I try using _ instead of using a named identifier?

scala> res0
res25: List[Int] = List(1, 2, 3, 4, 5)

scala> res0.map(_=>"item "+_.toString)
<console>:6: error: missing parameter type for expanded function ((x$2) => "item
 ".$plus(x$2.toString))
       res0.map(_=>"item "+_.toString)
                           ^

scala> res0.map(i=>"item "+i.toString)
res29: List[java.lang.String] = List(item 1, item 2, item 3, item 4, item 5)

回答1:


Underscores used in place of variable names like that are special; the Nth underscore means the Nth argument to an anonymous function. So the following are equivalent:

List(1, 2, 3).map(x => x + 1)

List(1, 2, 3).map(_ + 1)

But, if you do this:

List(1, 2, 3).map(_ => _ + 1) 

Then you are mapping the list with a function that ignores its single argument and returns the function defined by _ + 1. (This specific example won't compile because the compiler can't infer what type the second underscore has.) An equivalent example with named parameters would look like:

List(1, 2, 3).map(x => { y => y + 1 })

In short, using underscores in a function's argument list means "I am ignoring these arguments in the body of this function." Using them in the body means "Compiler, please generate an argument list for me." The two usages don't mix very well.




回答2:


To complement the other answers, here are some examples showing why you get the "missing parameter type" in some cases when using '_' as a placeholder parameter.

Scala's type inference considers the 'expected' type of an expression based on its context. If there is no context, it cannot infer the type of the parameters. Notice in the error message the first and second instances of _ are replaced with the compiler generated identifiers x$1 and x$2.

scala> _ + _
<console>:5: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
       _ + _
       ^
<console>:5: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
       _ + _
           ^

Adding a type ascription to the entire expression provides enough context to help the inferencer:

scala> (_ + _) : ((Int, Int) => Int)
res3: (Int, Int) => Int = <function2>

Alternatively, you can add a type ascription to each parameter placeholder:

scala> (_: Int) + (_: Int)          
res4: (Int, Int) => Int = <function2>

In the function call below with type arguments provided, the context is unambigous and the function type is inferred.

scala> def bar[A, R](a1: A, a2: A, f: (A, A) => R) = f(a1, a2)  
bar: [A,R](a1: A,a2: A,f: (A, A) => R)R

scala> bar[Int, Int](1, 1, _ + _)
res5: Int = 2

However, if we ask the compiler to infer the type parameters, if fails:

scala> bar(1, 1, _ + _)          
<console>:7: error: missing parameter type for expanded function ((x$1, x$2) => x$1.$plus(x$2))
       bar(1, 1, _ + _)
                 ^
<console>:7: error: missing parameter type for expanded function ((x$1: <error>, x$2) => x$1.$plus(x$2))
       bar(1, 1, _ + _)
                     ^

We can help it, though, by currying the parameter lists. Here, the arguments to the first parameter list (1, 1), tell the inference that the type parameter A should be Int. It then knows that the type of the argument f must be (Int, Int) => ?), and the return type R is inferred as Int, the result of integer addition. You will see the same approach used in Traversable.flatMap in the standard library.

scala> def foo[A, R](a1: A, a2: A)(f: (A, A) => R) = f(a1, a2) 
foo: [A,R](a1: A,a2: A)(f: (A, A) => R)R

scala> foo[Int, Int](1, 1) { _ + _ }
res1: Int = 2

scala> foo(1, 1) { _ + _ }
res0: Int = 2



回答3:


If you're not going to bind an identifier, just leave that part out.

res0.map("item "+_.toString)


来源:https://stackoverflow.com/questions/2353222/in-scala-what-is-the-difference-between-using-the-and-using-a-named-identif

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!