This question is heavily related to my other question (and may lead to me solving that one) but is definetely different.
how to allow passing in a => AnyRef function and call that function
I have been playing with different function creations and I am frankly having trouble creating an anonymous function of type => AnyRef and => String. I can create a function of type () => AnyRef and () => String I think.
Example 1
I have the following code
def debugLazyTest2(msg: => String) : Unit = {
System.out.println(msg)
}
//and client code
val function: () => String = () => {
executed = true
"asdf"+executed+" hi there"
}
log2.debugLazyTest2(function)
but the compile error says found: () => String which makes sense but then says "required: String" instead of "required: => String"
What is going on here?
Example 2 to get even more bizarre, I have this code which compiles while above did not compile
def debugLazyTest(msg: => AnyRef) : Unit = {
System.out.println(msg.toString)
}
//and client code which compiles!!!!
val function: () => AnyRef = () => {
executed = true
"asdf"+executed+" hi there"
}
log2.debugLazyTest(function)
This code compiles though it doesn't work the way I would like as the library can't seem to invoke the function before toString is called (that is in my other thread and is a separate question).
Any ideas as to what is going on here?
thanks,
Dean
If you wrote this it would work:
log2.debugLazyTest2(function())
msg is a by-name parameter, not a function. You have to pass in an expression of type String (or AnyRef in the second example)
The second example compiles because the () => AnyRef you pass in is actually also an AnyRef, since a function is an AnyRef. But then it is the function itself that is printed, not the result of executing it.
Consider the following code:
scala> def test(x: String) = debugLazyTest2(x)
test: (x: String)Unit
If we then run (in Scala 2.11.2):
:javap test
And trim a bit of the generated output, we see the following:
public void test(java.lang.String);
flags: ACC_PUBLIC
Code:
stack=4, locals=2, args_size=2
0: getstatic #19 // Field .MODULE$:L;
3: new #21 // class $anonfun$test$1
6: dup
7: aload_1
8: invokespecial #23 // Method $anonfun$test$1."<init>":(Ljava/lang/String;)V
11: invokevirtual #27 // Method .debugLazyTest2:(Lscala/Function0;)V
14: return
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this L;
0 15 1 x Ljava/lang/String;
LineNumberTable:
line 8: 0
And then we consider the signature for debugLazyTest2
:
public void debugLazyTest2(scala.Function0<java.lang.String>);
The key line is:
3: new #21 // class $anonfun$test$1
If I am reading the code correctly, we are instantiating a new instance of the class $anonfun$test$1
- and passing that new anonymous Function0[String]
to debugLazyTest2
. This makes our test
method equivalent to the following:
def test(x: String) = debugLazyTest2(new Function0[String] {
def apply(): String = x
})
When we consider passing in an instance of Function0[String]
to debugLazyTest2
making the same transformation, we get:
debugLazyTest2(function)
which becomes:
debugLazyTest2(new Function0[String] {
def apply(): Function0[String] = function
})
which, of course, does not compile, because apply(): Function0[String]
does not match the required type apply(): String
(hence the error message).
Actually invoking the function instead of returning it works:
debugLazyTest2(function())
becomes:
debugLazyTest2(new Function0[String] {
def apply(): String = function()
})