soupybionics
soupybionics

Reputation: 4386

Understanding how any function definition in scala evaluates to a trait

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

Answers (3)

Victor Moroz
Victor Moroz

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

soupybionics
soupybionics

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

dveim
dveim

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

Related Questions