Function literals vs function values - scala

Trying to figure out the significance of this section from Programming in Scala, 2nd edition.
A function literal is compiled into a class that when instantiated at
run- time is a function value. Thus the distinction between function
literals and values is that function literals exist in the source
code, whereas function val- ues exist as objects at runtime. The
distinction is much like that between classes (source code) and
objects (runtime).
I don't really get what they're trying to say here. That function values don't exist in the source code and function literals don't exist at runtime?
// literal
val addOne = (x: Int) => x + 1
// value
def add1(x: Int): Int = x + 1
I can pass either to another function:
def doThing(thing: Int => Int) = thing(5)
doThing(addOne) // 6
doThing(add1) // 6
It also appears that function literals are placed into a class that inherits from FunctionN (where N is the arity of the function). What distinction are they trying to make here?

I don't really get what they're trying to say here.
Your example of a function literal and value aren't accurate. The book is not comparing methods to functions, it's creating a distinction between two different "modes" of a function. The hint is in the first sentence:
A function literal is compiled into a class that when instantiated at
run-time is a function value.
When at compile time you type:
val addOne = (x: Int) => x + 1
This is what the book refers to as a "function literal" (or Anonymous Function). The same way you have a string literal by typing:
val s = "Hello, World"
addOne to the compiler is a Function1[Int, Int], meaning takes an Int and returns an Int result. The function literal syntax ((x: Int) => x + 1) is syntactic sugar over FunctionN, where N is determined by the arity of the function.
At run-time, the compiler takes this "function literal" and "puts life into it" by instantiating an object of type Function1[Int, Int], thus creating a function value which you can invoke, pass around, etc.
What distinction are they trying to make here?
The book is basically trying to create a distinction between the compile time and runtime representation of a function, so when they say "function literal" you'll understand they're talking about the former, and when they say "function value" the latter.

Scala code compiles into jvm bytecode which does not have functions but has classes. So your code val addOne = (x: Int) => x + 1 is syntactic sugar for (I'm not precise here, only high concepts):
final class anonFun extends scala.runtime.AbstractFunction1 {
final def apply(x: Int) = x + 1
}
val addOne = new anonFun();
Actually compiled files are much more complex, you can compile your scala file with scalac -print and see desugared scala code. Here is my compile output of small object:
object Main {
val addOne = (x: Int) => x + 1
}
(scalac version 2.11.7)
package <empty> {
object Main extends Object {
private[this] val addOne: Function1 = _;
<stable> <accessor> def addOne(): Function1 = Main.this.addOne;
def <init>(): Main.type = {
Main.super.<init>();
Main.this.addOne = {
(new <$anon: Function1>(): Function1)
};
()
}
};
#SerialVersionUID(value = 0) final <synthetic> class anonfun$1 extends scala.runtime.AbstractFunction1$mcII$sp with Serializable {
final def apply(x: Int): Int = anonfun$1.this.apply$mcII$sp(x);
<specialized> def apply$mcII$sp(x: Int): Int = x.+(1);
final <bridge> <artifact> def apply(v1: Object): Object = scala.Int.box(anonfun$1.this.apply(scala.Int.unbox(v1)));
def <init>(): <$anon: Function1> = {
anonfun$1.super.<init>();
()
}
}
}
At the contrast look at compiling
object Main {
def addOne(i: Int) = i + 1
}
(scalac version 2.11.7)
package <empty> {
object Main extends Object {
def addOne(i: Int): Int = i.+(1);
def <init>(): Main.type = {
Main.super.<init>();
()
}
}
}
You can see that def addOne becomes just an instance method of an object, but val addOne is itself an object.

There's nothing special about functions in that paragraph. It applies exactly the same to other kinds of literals, e.g. integers.
The specific character sequence composed of the character with the ASCII value 0x34 followed by the character with the ASCII value 0x32 and surrounded by whitespace, parentheses, comma, semicolon, or some other kind of delimiter, is an integer literal.
It will be compiled into an instance of the Int class. (Actually, that's not quite true: on the JVM implementation of Scala, it will be compiled into a JVM primitive int, and on the ECMAScript implementation of Scala, it will be compiled into an ECMAScript Number, and the compiler will generate additional code to "fake" it being an instance of the scala.Int class.)
At runtime, it will be instantiated into an integer representing the value 42.
Functions are no different here. The specific sequence of characters that make up a literal (whether that be a function literal, integer literal, string literal, character literal, symbol literal, tuple literal, boolean literal, or the literal value null) exist only in the source code. The values those literals represent exist only at runtime. The compile time representation of those literals is as an instance of some class.

Related

How is a function a value but a method id not?

Why are functions values but methods are not, or what makes a function a value in particular? What would allow me to decipher that methods do not contain that characteristic?
Scala is an object-oriented language. In object-oriented languages, every value is an object, and every object is a value.
Methods are bound to objects. It is awkward to have methods at the same time be a building block of objects and be objects themselves. It is easier to have them not be objects.
A function in Scala is, in some sense, just an object with an apply method. If there is no method named foo in scope, then foo() is simply syntactic sugar for foo.apply(). So, functions are values because they are objects.
While any object that has an apply method can be called as if it were a function, when we talk about "functions" in Scala, we usually mean something more specific: an instance of one of the FunctionN traits such as Function2[-T1, -T2, +R]. In particular, the function literal syntax
val add = (a: Int, b: Int) => a + b
is syntactic sugar for
val add = new Function2[Int, Int, Int] {
override def apply(a: Int, b: Int) = a + b
}
And the function type
type F = (Int, String, Long) => Boolean
is syntactic sugar for
type F = Function3[Int, String, Long, Boolean]
[Scastie link]
where each of the FunctionN traits is defined like this:
package scala
trait Function0[+R] {
def apply: R
override def toString = "<function>"
}
trait Function1[-T, +R] {
def apply(x: T): R
override def toString = "<function>"
}
trait Function2[-T1, -T2, +R] {
def apply(x1: T1, x2: T2): R
override def toString = "<function>"
}
trait Function3[-T1, -T2, -T3, +R] {
def apply(x1: T1, x2: T2, x3: T3): R
override def toString = "<function>"
}
and so on.
It is possible to convert a method into a function value using η-expansion. This can be done explicitly using a trailing underscore:
val f = println _
or in some cases, when it is clear that a function value is required, even by just using the bare name of the method:
val it = Iterable(1, 2, 3)
it.foreach(println)
[Scastie link]
Note that this is not much different from other languages. In Java, C#, and Ruby, for example, it is the same thing: methods are defined as part of classes (or structs in C# and modules in Ruby) and are bound to objects, but aren't objects themselves. Instead, you have a separate notion of a function (instance of a SAM interface / functional interface in Java, Action or Func in C#, Proc in Ruby), which is an object.
In Ruby, you can create a proxy object for a method that is bound to an object which has the same interface as Proc.
Other languages make different choices, i.e. in ECMAScript and Python, methods are objects / values, but they are not as tightly bound to objects as they are in Scala, Java, C#, and Ruby. Instead, methods are basically normal functions that are assigned to fields of the object.
One of the defining characteristics of a value is that it can be assigned to a variable. A method, technically speaking, cannot be assigned to a variable, for example
def m(i: Int): Int = i + 1
val f = m // error
However there is a process called eta expansion that converts a method to a function value. In Scala 2, we can trigger it by providing type ascription to a variable
val f: Int => Int = m
or by using underscore _
val f = m _
However this restriction has been lifted in Scala 3 (Dotty) which now provides automatic eta expansion so the following compiles successfully
scala> def f(i: Int): Int = i + 1
| val x = f
def f(i: Int): Int
val x: Int => Int = Lambda$1218/158882051#62cf6a84

What is the eta expansion in Scala?

I am new to Scala. I just heard the term "eta expansion" and roughly know that it means to expand a method to a function object. But I find few resources in SO that systematically introduce it.
I am curious about how eta expansion works in Scala. What are the scenarios that eta expansion are needed? And how eta expansion is implemented in Scala?
I roughly know that in cases like this:
def someMethod(x: Int): Int = x * x
someMethod _ will roughly be translated to a new function object like this:
new Function1[Int, Int] {
def apply(x: Int): Int = x * x
}
Is it all that Scala does?
The definition, and some examples, are given in http://scala-lang.org/files/archive/spec/2.11/06-expressions.html#method-values.
someMethod _ will roughly be translated to a new function object like this:
Not quite: it's actually
new Function1[Int, Int] {
def apply(x: Int): Int = someMethod(x)
}
The difference matters e.g. if someMethod is overridden somewhere.
Is it all that Scala does?
You also need to take into account what happens if the method takes multiple parameter lists (you get a function which returns a function) or by-name parameters.
What are the scenarios that eta expansion are needed?
When you specifically ask for it (e.g. someMethod _).
When you use a method (with parameters) where a value of a function type (or a SAM type in Scala 2.12) is expected. E.g.
def foo(f: Int => Int) = ???
foo(someMethod)
That's it.
Note that using eta-expansion and an anonymous function with placeholders (someMethod(_)) can behave differently due to type inference, implicits, etc.
Eta expansion In high level, is a process of translating methods into functions. Why? What? Aren't them the same? Let's explain:
A method in scala is what we know as def someMethodName(SomePramList): SomeReturnType. It starts with def. It may have parameter list, or even maybe more then 1. For example:
def numAdder(num1: Int)(num2: Int): Int =
num1 + num2
A function, or lambda function looks something like: (SomeParams) => SomeReturnType. For example:
val aFunction: Int => Int => Int = (num1: Int) => (num2: Int) => num1 + num2
Important to understand about functions is that this syntax is basically a syntactic sugar to FunctionN.apply method.
What are the scenarios that eta expansion are needed?
Some examples:
Example1 - Applying a method inside map (or filter, flatMap etc)
Writing such code:
def addPlus1(x: Int): Int = x + 1
List(1,2,3).map(addPlus1)
The compiler needs to have a function inside the map. So, it transforms the method given into a function:
List(1,2,3).map(x => addPlus1(x)). This is Eta expansion.
Example2 - currying
When defining curried method, for example:
def numAdder(num1: Int)(num2: Int): Int =
num1 + num2
And them creating a function like:
val curriedFunction: Int => Int = numAdder(4)
//or
val curriedFunction2 = numAdder(4) _
We defined a function out of a method. This is Eta expansion.
Some more examples
Defined a method which accepts a function value:
def someMethod(f: () => Int): Int = f()
def method(): Int = 10
And then run:
someMethod(method)
will transform the method method into a function. This is Eta expansion

Magnet pattern and overloaded methods

There is a significant difference in how Scala resolves implicit conversions from "Magnet Pattern" for non-overloaded and overloaded methods.
Suppose there is a trait Apply (a variation of a "Magnet Pattern") implemented as follows.
trait Apply[A] {
def apply(): A
}
object Apply {
implicit def fromLazyVal[A](v: => A): Apply[A] = new Apply[A] {
def apply(): A = v
}
}
Now we create a trait Foo that has a single apply taking an instance of Apply so we can pass it any value of arbitrary type A since there an implicit conversion from A => Apply[A].
trait Foo[A] {
def apply(a: Apply[A]): A = a()
}
We can make sure it works as expected using REPL and this workaround to de-sugar Scala code.
scala> val foo = new Foo[String]{}
foo: Foo[String] = $anon$1#3a248e6a
scala> showCode(reify { foo { "foo" } }.tree)
res9: String =
$line21$read.foo.apply(
$read.INSTANCE.Apply.fromLazyVal("foo")
)
This works great, but suppose we pass a complex expression (with ;) to the apply method.
scala> val foo = new Foo[Int]{}
foo: Foo[Int] = $anon$1#5645b124
scala> var i = 0
i: Int = 0
scala> showCode(reify { foo { i = i + 1; i } }.tree)
res10: String =
$line23$read.foo.apply({
$line24$read.`i_=`($line24$read.i.+(1));
$read.INSTANCE.Apply.fromLazyVal($line24$read.i)
})
As we can see, an implicit conversion has been applied only on the last part of the complex expression (i.e., i), not to the whole expression. So, i = i + 1 was strictly evaluated at the moment we pass it to an apply method, which is not what we've been expecting.
Good (or bad) news. We can make scalac to use the whole expression in the implicit conversion. So i = i + 1 will be evaluated lazily as expected. To do so we (surprize, surprize!) we add an overload method Foo.apply that takes any type, but not Apply.
trait Foo[A] {
def apply(a: Apply[A]): A = a()
def apply(s: Symbol): Foo[A] = this
}
And then.
scala> var i = 0
i: Int = 0
scala> val foo = new Foo[Int]{}
foo: Foo[Int] = $anon$1#3ff00018
scala> showCode(reify { foo { i = i + 1; i } }.tree)
res11: String =
$line28$read.foo.apply($read.INSTANCE.Apply.fromLazyVal({
$line27$read.`i_=`($line27$read.i.+(1));
$line27$read.i
}))
As we can see, the entire expression i = i + 1; i made it under the implicit conversion as expected.
So my question is why is that? Why the scope of which an implicit conversion is applied depends on the fact whether or not there is an overloaded method in the class.
Now, that is a tricky one. And it's actually pretty awesome, I didn't know that "workaround" to the "lazy implicit does not cover full block" problem. Thanks for that!
What happens is related to expected types, and how they affect type inference works, implicit conversions, and overloads.
Type inference and expected types
First, we have to know that type inference in Scala is bi-directional. Most of the inference works bottom-up (given a: Int and b: Int, infer a + b: Int), but some things are top-down. For example, inferring the parameter types of a lambda is top-down:
def foo(f: Int => Int): Int = f(42)
foo(x => x + 1)
In the second line, after resolving foo to be def foo(f: Int => Int): Int, the type inferencer can tell that x must be of type Int. It does so before typechecking the lambda itself. It propagates type information from the function application down to the lambda, which is a parameter.
Top-down inference basically relies on the notion of expected type. When typechecking a node of the AST of the program, the typechecker does not start empty-handed. It receives an expected type from "above" (in this case, the function application node). When typechecking the lambda x => x + 1 in the above example, the expected type is Int => Int, because we know what parameter type is expected by foo. This drives the type inference into inferring Int for the parameter x, which in turn allows to typecheck x + 1.
Expected types are propagated down certain constructs, e.g., blocks ({}) and the branches of ifs and matches. Hence, you could also call foo with
foo({
val y = 1
x => x + y
})
and the typechecker is still able to infer x: Int. That is because, when typechecking the block { ... }, the expected type Int => Int is passed down to the typechecking of the last expression, i.e., x => x + y.
Implicit conversions and expected types
Now, we have to introduce implicit conversions into the mix. When typechecking a node produces a value of type T, but the expected type for that node is U where T <: U is false, the typechecker looks for an implicit T => U (I'm probably simplifying things a bit here, but the gist is still true). This is why your first example does not work. Let us look at it closely:
trait Foo[A] {
def apply(a: Apply[A]): A = a()
}
val foo = new Foo[Int] {}
foo({
i = i + 1
i
})
When calling foo.apply, the expected type for the parameter (i.e., the block) is Apply[Int] (A has already been instantiated to Int). We can "write" this typechecker "state" like this:
{
i = i + 1
i
}: Apply[Int]
This expected type is passed down to the last expression of the block, which gives:
{
i = i + 1
(i: Apply[Int])
}
at this point, since i: Int and the expected type is Apply[Int], the typechecker finds the implicit conversion:
{
i = i + 1
fromLazyVal[Int](i)
}
which causes only i to be lazified.
Overloads and expected types
OK, time to throw overloads in there! When the typechecker sees an application of an overload method, it has much more trouble deciding on an expected type. We can see that with the following example:
object Foo {
def apply(f: Int => Int): Int = f(42)
def apply(f: String => String): String = f("hello")
}
Foo(x => x + 1)
gives:
error: missing parameter type
Foo(x => x + 1)
^
In this case, the failure of the typechecker to figure out an expected type causes the parameter type not to be inferred.
If we take your "solution" to your issue, we have a different consequence:
trait Foo[A] {
def apply(a: Apply[A]): A = a()
def apply(s: Symbol): Foo[A] = this
}
val foo = new Foo[Int] {}
foo({
i = i + 1
i
})
Now when typechecking the block, the typechecker has no expected type to work with. It will therefore typecheck the last expression without expression, and eventually typecheck the whole block as an Int:
{
i = i + 1
i
}: Int
Only now, with an already typechecked argument, does it try to resolve the overloads. Since none of the overloads conforms directly, it tries to apply an implicit conversion from Int to either Apply[Int] or Symbol. It finds fromLazyVal[Int], which it applies to the entire argument. It does not push it inside the block anymore, giving:
fromLazyVal({
i = i + 1
i
}): Apply[Int]
In this case, the whole block is lazified.
This concludes the explanation. To summarize, the major difference is the presence vs absence of an expected type when typechecking the block. With an expected type, the implicit conversion is pushed down as much as possible, down to just i. Without the expected type, the implicit conversion is applied a posteriori on the entire argument, i.e., the whole block.

What does treating a method as a function mean in Scala?

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?
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.
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 m type is (List[Int])AnyRef
into (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 AbstractFunction1 class 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.

Scala expression evaluation

I'm intrigued by the following Scala compiler behavior. When i declare a function of type unit, but nevertheless provide as body a function that evaluate to an Int, the Scala compiler is ok with it.
def add(x:Int, y:Int) = x + y
def main(args: Array[String]): Unit = {add(args(0).toInt, args(0).toInt)}
While the same is not true with other type such in
def afunc: String = {1} //type mismatch; found : Int(1) required: String
Also if i write
def afunc: Unit = {1}
or
def main(args: Array[String]): Unit = {2 + 2} // Which is just like the first addition above
In both case i get the following warning:
a pure expression does nothing in statement position; you may be omitting necessary parentheses
In a sense there is 2 questions here. What is the difference between a function that return evaluate to an Int and the expression 2 + 2. Then why on earth, the compiler does not complain that a function that is suppose to evaluate to Unit, gets a body that evaluate to another type, as it would happens between other types.
Many thanks in advance,
Maatari
What is the difference between a function that return evaluate to an
Int and the expression 2 + 2.
A function that evaluates to an Int might have side effects
var a = 0
def fn: Int = {
a = a + 1
1
}
Every call to fn changes the value of a.
Then why on earth, the compiler does not complain that a function that
is suppose to evaluate to Unit, gets a body that evaluate to another
type, as it would happens between other types.
When you specify Unit as the return type the compiler does an implicit conversion from whatever value the function returns to Unit (there is only one Unit value as it is an object, you can think of it as the void type of Scala).
Unit is inferred as a return type for convenience. For example,
scala> val h = new collection.mutable.HashMap[String,String]
h: scala.collection.mutable.HashMap[String,String] = Map()
scala> h += "fish" -> "salmon"
res1: h.type = Map(fish -> salmon)
we see that the += method on mutable HashMaps returns the map.
But if you don't infer unit, then
def add(a: String, b: String): Unit = h += a -> b
doesn't work.
Which thing is worse--accidentally expecting a return value vs. demanding you explicitly return the most boring possible return value--depends on how functional your code is. If you have more than a few side-effects, not inferring Unit is rather painful.
A function with just the parentheses is mainly called for the side-effects. It may return a result, as it was common in the days of C or Java but in reality we don't care, cause we will not use it.
{} is equivalent to : Unit =, and the annotation of return type can be seen as a cast or an implicit conversion to match the type expected by the function.
Here is another example where this behavior occurs.
def add(a: Int, b:Int) : Double = a + b
add: (a: Int, b: Int)Double
scala> add(4,5)
res17: Double = 9.0
Thus it seems that every value can be transformed to unit.
scala> val a: Unit = 1:Unit
<console>:28: warning: a pure expression does nothing in statement position; you may be omitting necessary parentheses
val a: Unit = 1:Unit
^
a: Unit = ()