Reputation: 149
Why executeCommand
is accepting the callBack
function of wrong return type?
import scala.sys.process._
object Test {
def executeCommand(x: String)(y: Int => Unit) = {
def matchStrToInt(str: String) = {
str match {
case "true" => 1
case "false" => 2
}
}
y(matchStrToInt(x))
} //> executeCommand: (x: String)(y: Int => Unit)Unit
executeCommand("true")(callBack)
def callBack(x: Int): Int = {
x
} //> callBack: (x: Int)Int
}
As far as I know Scala is strictly statically typed language. Can someone explain the reason behind it?
Upvotes: 0
Views: 111
Reputation: 15086
I think what is happening here is that two separate mechanisms in the Scala compiler are working together:
This is basically just a way to avoid having to explicitly write ()
, the Unit
value, every time Unit
is the expected type. Like at the end of a while
loop, a Unit
returning method, a for(a <- list){ ... }
expression, etc.
It rewrites (in memory during compilation, not on disk) code like this
def foo: Unit = 42
to this
def foo: Unit = { 42; () }
This is how methods in Scala are converted to functions. A method is a JVM construct, an element of a JVM classfile. A function is just a value, an instance of a scala.FunctionN
class. You can pass around functions as values (because they are values), but you can't pass around methods as values (because they're not).
So basically the compiler does the following transformation:
def foo(i: Int): Int = i
def bar(f: Int => Int) = f
bar(foo)
~~~>
bar( (x1: Int) => foo(x1) )
So when you have this code:
def foo(i: Int): Int = i
def bar(f: Int => Unit) = f
bar(foo)
The compiler will first use eta expansion to convert foo
to a function.
bar( (x1: Int) => foo(x1) )
And then it will see that foo(x1)
is an expression of type Int
while an expression of type Unit
is expected there. So it will apply value discarding.
bar( (x1: Int) => { foo(x1); () } )
You can check that this "problem" only occurs when converting methods to functions:
scala> def bar(f: Int => Unit) = f
bar: (f: Int => Unit)Int => Unit
scala> val foo = (i: Int) => i
foo: Int => Int = <function1>
scala> bar(foo)
<console>:14: error: type mismatch;
found : Int => Int
required: Int => Unit
bar(foo)
^
Upvotes: 4