RussAbbott
RussAbbott

Reputation: 2738

What is the difference between these two expressions in a Scala anonymous function?

This seems like a minor issue since there is a simple work-around, but what is the difference between these two expressions?

List(1, 2, 3).sortBy(_)
List(1, 2, 3).sortBy(x => x)

The first gets a compiler diagnostic

missing parameter type for expanded function ((x$1) => List(1, 2, 3).sortBy(x$1))

The second is treated as syntactically valid.

Thanks.

Upvotes: 2

Views: 340

Answers (2)

Alex
Alex

Reputation: 1470

I think part of the confusion is that an underscore can mean many things in Scala. Among the many things it can mean are:

  1. partially applied function
  2. anonymous function parameter

The Scala compiler is interpreting your first example as a partially applied function. Since there is no function to pass function parameters into, it is instead treating the underscore as a partially applied function:

scala> def opposite1(x: Int): Int = -x
opposite1: (x: Int)Int

scala> def opposite2: Int => Int = (x: Int) => -x // means the same as opposite1
opposite2: Int => Int

scala> def transform(x: Int, f: Int => Int): Int = f(x)
transform: (x: Int, f: Int => Int)Int

scala> transform(2, opposite2)
res6: Int = -2

So, transform is a function that takes an Int and a function that takes an Int and returns an Int. I can partially apply the function by passing in just one of the paramters, but the problem is that the compilier doesn't try to do any type inference:

scala> transform(_, opposite2)
<console>:10: error: missing parameter type for expanded function 
                         ((x$1) => transform(x$1, opposite2))
              transform(_, opposite2)

I can fix this by providing an explicit type declaration:

scala> transform(_: Int, opposite2)
res8: Int => Int = <function1>

scala> transform(2, _: Int => Int)
res9: (Int => Int) => Int = <function1>

In the case of your original example, it will compile if I provide a type declaration:

scala> List(1,2,3).sortBy(_: Int => Int)
res10: (Int => Int) => List[Int] = <function1>

So, res10 is a function that takes a single argument: a function that takes an Int and returns an Int, and the return type of res10 is a List[Int] that contains 1, 2 & 3:

scala> res10(opposite2)
res11: List[Int] = List(3, 2, 1)

Bottom Line

Scala type inference is a very cool feature because it eliminates boiler plate code. HOWEVER, there are cases when the cost of typing a few extra characters to explicitly declare the type is well worth the investment.

Upvotes: 0

4lex1v
4lex1v

Reputation: 21567

The second one is simpler, it's just an identity function, which can be easily replace with identity:

List(1, 2, 3).sortBy(x => x)

You're basically asking Scala take an element from the list, apply a function f to it and sort the list with this results. It has the following signature:

def sortBy[B](f: (A) ⇒ B)(implicit ord: math.Ordering[B]): List[A]

The first one is a syntactic sugar for lambda (anonymous) function. In your example it doesn't work because of the desugaring rules, it resolves to the closes enclosing scope, this is written in the error message:

((x$1) => List(1, 2, 3).sortBy(x$1))

To make this work you can do like this:

def f[T](a: T) = a

List(1, 2, 3).sortBy(f(_))

This won't throw a compilation error, cause the compile will desugar it into:

List(1, 2, 3).sortBy((x$1) => f(x$1))

Or you can just write

List(1, 2, 3).sortBy(identity)

Where identity is identical to f function, it's defined in Predef.scala

Upvotes: 4

Related Questions