By assigning a variable (or value?) a method name with a space and an underscore, you tell scala to treat the method as a function, which apparently means doing more than simply taking the value generated by a call to the method and assigning to the variable. What else is/can go on through such an assignment?
-
2`foo _` is more like *package method foo into value* – om-nom-nom Apr 21 '15 at 13:05
-
1possible duplicate of classic [Difference between method and function in Scala](http://stackoverflow.com/questions/2529184/difference-between-method-and-function-in-scala) – dk14 Apr 21 '15 at 14:13
3 Answers
Since Scala runs on the JVM, it's easier to understand in terms of simple Java-like classes without Scala's syntactic sugar.
Remember that Scala functions are essentially members of a class similar to the following (signature deliberately simplified):
class Function[X, Y] {
def apply(x: X): Y
}
Application of a function f to an argument x is desugared into a method application f.apply(x).
Now suppose that you have another class Foo with method bar:
class Foo {
def bar(x: Int): String
}
If you now have an instance foo of type Foo, then whenever its method bar is transformed into a function by writing:
val f = foo.bar(_)
a new instance of an anonymous subclass of Function is created:
val f = new Function[Int, String] {
def apply(x: Int) = foo.bar(x)
}
If you use this syntax inside a class, this is closed over instead of an instance foo.
This is what all those weirdly named classes Main$$anon$1$$anonfun$1 are: they are the anonymous classes that represent functions. The functions can appear quite implicitly (for example, as blocks passed to the for-loops).
That's all there is to it semantically. The rest is just syntactic sugar.
Here is a complete runnable example that demonstrates the conversion of an instance method into a function:
- with sugar, from the outside (a)
- with sugar, from the inside (b)
- without sugar, from the outside (c)
- without sugar, from the inside (d)
You can save it into a file and execute with scala <filename.scala>:
/** A simple greeter that prints 'hello name' multiple times */
case class Hey(name: String) { thisHeyInst =>
def hello(x: Int): String = ("hello " + name + " ") * x
def withSugarFromInside = hello(_)
def noSugarFromInside = new Function[Int, String] {
def apply(y: Int) = thisHeyInst.hello(y)
}
}
val heyAlice = Hey("Alice")
val heyBob = Hey("Bob")
val heyCharlie = Hey("Charlie")
val heyDonald = Hey("Donald")
val a = heyAlice.hello(_)
val b = heyBob.withSugarFromInside
val c = new Function[Int, String] { def apply(y: Int) = heyCharlie.hello(y) }
val d = heyDonald.noSugarFromInside
println(a(3))
println(b(3))
println(c(3))
println(d(3))
In all four cases, a greeting is printed three times.
- 43,673
- 4
- 57
- 93
What _ actually does is an eta-conversion. It takes compile-time construction called method and returns runtime construction called anonymous function, which is actually an instance of scala's Function. Exactly the class depends on arity, so it might be Function1, Function2, Function3 and so on. The point here is to make First-class citizen, which may act like a value.
OOP needs a little more than some new object. Before making the code that creates instance, compiler generates a new class (extending FunctionN) in compile-time, but theoretically it shouldn't be necessary a whole new class. For Java 8 it could be native Java-lambdas.
Btw, you may extend Function1 by yourself and even eta-abstract it again:
scala> object f extends (Int => Int) { def apply(a: Int) = a }
scala> f(1)
res0: Int = 1
scala> f.apply _
res1: Int => Int = <function1>
scala> res1(5)
res2: Int = 5
As a conclusion a little copy-paste from @Daniel C. Sobral's answer:
the former can be easily converted into the latter:
val f = m _Scala will expand that, assuming
mtype is(List[Int])AnyRefinto (Scala 2.7):val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }On Scala 2.8, it actually uses an
AbstractFunction1class to reduce class sizes.
Or simply saying val f = m _ is same as val f = (x: List[Int]) => m(x)
To make this answer more modern and precise let's see what's happening using scalac 2.11.2 and javap:
$ echo "object Z{def f(a: Int) = a}" > Z.scala //no eta-abstraction here
$ scalac Z.scala
$ ls
Z$.class Z.class Z.scala
$ echo "object Z{def f(a: Int) = a; val k = f _}" > Z.scala
$ scalac Z.scala
$ ls
Z$$anonfun$1.class Z.class //new anonfun class added for lambda
Z$.class Z.scala
$ javap -c Z\$\$anonfun\$1.class
Compiled from "Z.scala" // I've simplified output a bit
public final class Z$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
public final int apply(int);
Code:
calling apply$mcII$sp(int)
public int apply$mcII$sp(int); //that's it
Code:
0: getstatic #25 // reading Field Z$.MODULE$:LZ$;, which points to `object Z`
3: iload_1
4: invokevirtual #28 // calling Method Z$.f
7: ireturn
public final java.lang.Object apply(java.lang.Object); //just boxed version of `apply`
Code:
unboxToInt
calling apply(int) method
boxToInteger
public Z$$anonfun$1();
Code:
AbstractFunction1$mcII$sp."<init>":()V //initialize
}
So it still extends AbstractFunction1
I'll try to provide some examples how a function or method are assigned to values with underscore.
If it's need to reference a zero-argument function
scala> val uuid = java.util.UUID.randomUUID _
uuid: () => java.util.UUID = <function0>
scala> uuid()
res15: java.util.UUID = 3057ef51-8407-44c8-a09e-e2f4396f566e
scala> uuid()
uuid: java.util.UUID = c1e934e4-e722-4279-8a86-004fed8b9090
Check how it's different when one does
scala> val uuid = java.util.UUID.randomUUID
uuid: java.util.UUID = 292708cb-14dc-4ace-a56b-4ed80d7ccfc7
In first case one assigned a reference to a function. And then calling uuid() generates new UUID every time.
In second, function randomUUID has been called and value assigned to a val uuid.
There are some other cases why _ might be useful. It's possible to use a function with two arguments and create a function with a single argument out of it.
scala> def multiply(n: Int)(m: Int) = n*m
multiply: (n: Int)(m: Int)Int
scala> val by2 = multiply(2) _
by2: Int => Int = <function1>
scala> by2(3)
res16: Int = 6
To be able to do, it's crucial to define function multiply as curried. It's called function currying.
- 7,140
- 15
- 18