Scala Tutorials Part #20 - Internals of Functions in the JVM


Internals of functions

In the last part we saw how functional programming came into existence with inspiration from lambda calculus. In this article we will look at how functions are implemented in the JVM, since it does not natively support functional programming. This knowledge might not be really necessary in the life of a day to day developer but will come in handy when dealing with obscure bugs.

This is part 20 of the Scala tutorial series. Check here for the full series.

Index

Decompiling Functions

Let’s consider the simple square function again that we saw in the last part.

def square : Int => Int = (x:Int) => x*x

If we take a look at the decompiled code then it translates to,

public static scala.Function1<java.lang.Object, java.lang.Object> square();
    Code:
       0: getstatic     #16                 // Field RunExample$.MODULE$:LRunExample$;
       3: invokevirtual #42                 // Method RunExample$.square:()Lscala/Function1;
       6: areturn

We can see that it gets converted to an instance of the Function1 trait. This trait extends the AnyRef class which is the root class of all reference types. This leads us to the understanding that all functions in Scala are objects in some form, in this case it is an instance of the FunctionN trait.

Another experiment we can perform is create a test class with a function.

class Run {

  def square(i:Int) : Int = i*i

  val squared = (i:Int) => i*i

}

If we compile this it generates two files i.e

The Run$$anonfun$1.class is the function class. By decompiling it, we can understand even more.

public final class Run$$anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp implements scala.Serializable {
  public static final long serialVersionUID;
  public final int apply(int);
  public int apply$mcII$sp(int);
  public final java.lang.Object apply(java.lang.Object);
  public Run$$anonfun$1(Run);
}

Most of it is boiler plate for the JVM such as a serializable, apply and a constructor.

The FunctionN trait

The example we saw earlier was evaluating to a trait called Function1, interestingly there are similar traits ranging from Function1 to Function22. Whoa !! So do the Scala developers put in 22 classes manually? Well the answer is obviously no and notice on the top of source code comments it says,

// GENERATED CODE: DO NOT EDIT. See scala.Function0 for timestamp.

Meaning it is generated from somewhere and not hand coded manually. Another thing to note is that the trait is using something what we would call the java equivalent of generics to handle heterogeneous types. We will see generics in detail in a later tutorial.

Next question that will come up is what will happen if there are functions with more than 22 parameters? You would notice that this number 22 was also present in case classes prior to 2.11 and was removed in 2.11 along with a few edge cases but it lives on in Functions and Tuples. In a real world scenario, hitting the 22 limit is rare and can be overcome by using nested structures such as Tuples or use other data structures such as HList (will explore later). Nested tuples however makes more sense and gives a natural structure.

Developers must keep in mind that the internals of how a function is implemented can change anytime but the core concept of a function being an object is not going to change much.

Functions vs Methods

Even though the syntax of both methods and functions start with the def keyword, now we know that they are not the same.

Methods belong to the object oriented world and has nothing to do with functions. In fact the very question of how methods are different from functions does not even make sense once you understand what functions are how the generated bytecode looks like.

Interoperability with OOP

Since each function is an object by itself, it can easily work in tandem with regular imperative/object oriented code. For example, let’s consider the already defined square function.

def square : Int => Int = (x:Int) => x*x

This takes an integer as a parameter and returns an integer and can be consumed like a regular method. To a programmer looks like a simple method while it is actually a function behind the scenes.

We have come to the end of this part and have learnt quite a lot of functional programming concepts in the last part and this one. Learning the internals of functions might not be necessary but can definitely make you feel at home(JVM).


Tagged Under


Scala


Search this website...




Keeping up with blogs...

I blog occasionally and mostly write about Software engineering and the likes. If you are interested in keeping up with new blog posts, you should follow me on twitter where I usually tweet when I publish them. You can also use the RSS feed , or even subscribe via email below.

Feedio Subscribe


Share & Like

If you like this post, then you can either share/discuss/vote up on the below sites.



Thoughts ...

Please feel free to share your comments below for discussion

Blog comments powered by Disqus