A function in Scala is an object that implements one of the FunctionN
traits. For example:
scala> def f(x: Int) = x * x
f: (x: Int)Int
scala> val ff = f _
ff: Int => Int = <function1>
scala> val fff: Function1[Int, Int] = f _
fff: Int => Int = <function1>
So far, so good. But what if we have a function that takes a by-name parameter? It certainly does still implement one of the FunctionN
traits:
scala> def g(x: => Int) = x * x
g: (x: => Int)Int
scala> val gg = g _
gg: => Int => Int = <function1>
scala> gg.isInstanceOf[Function1[_, _]]
res0: Boolean = true
But what type is it, exactly? It's not Function1[Int, Int]
:
scala> val ggg: Function1[Int, Int] = g _
<console>:8: error: type mismatch;
found : => Int => Int
required: Int => Int
val ggg: Function1[Int, Int] = g _
^
Nor is it Function1[Function0[Int], Int]
:
scala> val ggg: Function1[Function0[Int], Int] = g _
<console>:8: error: type mismatch;
found : => Int => Int
required: () => Int => Int
val ggg: Function1[Function0[Int], Int] = g _
^
And Function1[=> Int, Int]
fails to compile:
scala> val ggg: Function1[=> Int, Int] = g _
<console>:1: error: identifier expected but '=>' found.
val ggg: Function1[=> Int, Int] = g _
^
So what is it?
By-name is very useful but unsafe outside the type system
Scala by-name parameters are a syntactic sugar to make code more readable when lazy evaluation is needed. Without it we would need to put "() =>" in front of everything that needed to be lazy. That said, while it is just a function0 at runtime, it would be problematic at the typing system level if you could define anything other than a parameter as having a by-name type. Also remember that the FunctionN traits are there mostly for implementation and Java interoperability since there is no such thing as a function type in Java and the JVM.
Being explicit
If you do need to be explicit in your typing the following will allow you to be restrictive
def g(x: => Int) = x * x
val ggg: (=> Int) => Int = g _
More complex typing
The by-name typing can only be used inside the parameter portion of function type declarations. The function types themselves can then be used inside other parametrized types.
var funks: List[(=> Int) => Int] = Nil
funks ::= ggg
funks foreach { _ { println("hi"); 5 } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With