Reputation: 4386
I am currently looking into a couple scala code bases (each was maintained by different set of people across different geographical locations) and I feel that Scala offers different ways to do the same thing. My first hand understanding has been that Scala has a pretty thick layer of syntactic sugar code, so my immediate goal now is to penetrate that layer and see what are the fundamental building blocks.
As I understand after reading some docs, every function definition in scala is basically a trait with a filled "apply" method.(analogous to interface with anonymous body/class in java). Setting this as the basis (please correct me if my base understanding itself is wrong), I am playing around with function definitions in Scala.
Consider this:
def func (s:String):Int = {
100
}
evaluates to
new Function1[String, Int] {
def apply(s:String):Int = {
100
}
}
But what(and how) does the following evaluate to a trait definition?
def func (body: => Boolean):Int = {
body;
100;
}
What the "new Function ... " code look like in this case?
Upvotes: 1
Views: 218
Reputation: 9225
First of all
def func (s:String):Int = {
100
}
is not a function, it's a method, it has no value and doesn't evaluate to anything by itself. When placed in a functional context it does expand to a function, but to a different one (Scala creates a wrapper):
new Function1[String, Int] {
def apply(s:String):Int = {
func(s)
}
}
So if you want to eta-expand (Scala's term for converting a method to a function)
def func (body: => Boolean):Int = {
body;
100;
}
it should be something like:
new Function1[=> Boolean, Int] {
def apply(body: => Boolean):Int = {
func(body)
}
}
and Scala shows type as:
(=> Boolean) => Int
but the problem here is that by-name types can't be used as type parameters, so it looks like there is no legal way to define this function using Function1
trait.
UPDATE
Looks like there is a workaround by using lambda syntax (I can't declare the type of body
explicitly though, by-name is not allowed there):
var expandedFunc: (=> Boolean) => Int = body => func(body)
Upvotes: 1
Reputation: 4386
With the help of Dviem's comments, the code should look something like this:
val func = new Function1[()=>Boolean, Int] {
def apply(fn:()=>Boolean):Int = { fn(); 100 }
}
>>> func: (() => Boolean) => Int = <function1>
func (new Function0[Boolean] {
def apply():Boolean = { println ("returning true"); true }
})
>>> returning true
>>> res0: Int = 100
Upvotes: 0
Reputation: 3482
In case of confusion, look in javap
output. Scala's REPL gives convenient way to check it:
scala> def func (body: => Boolean):Int = {
| body;
| 100;
| }
func: (body: => Boolean)Int
scala> :javap -c func
Compiled from "<console>"
public class {
public static final MODULE$;
public static {};
Code:
0: new #2 // class
3: invokespecial #12 // Method "<init>":()V
6: return
public int func(scala.Function0<java.lang.Object>);
Code:
0: aload_1
1: invokeinterface #20, 1 // InterfaceMethod scala/Function0.apply$mcZ$sp:()Z
6: pop
7: bipush 100
9: ireturn
public ();
Code:
0: aload_0
1: invokespecial #24 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #26 // Field MODULE$:L;
8: return
}
So here you can see that body: => Boolean
is represented as scala.Function0<java.lang.Object>
in bytecode.
I'm using scala 2.11.8
version here. Scala 2.12
will try to reuse java8's features like default methods (in interfaces) or SAMs as much as possible (you can try -Xexperimental
flag, that enables introp for 2.11
). That will lead to another bytecode (incompatible with java < 8) and better performance. You can read more about 2.12
changes here.
Upvotes: 3