I'm trying to use a class to aggregate as a single interface a bunch of proxies for API functions. A couple of these are nullary functions, but I don't want the act of including them in the class constructor to trigger the API calls. My solution right now is to wrap the call in a literal nullary function new myClass(() => apiCall)
and then calling the member function explicitly. This isn't all that bad, but I'm wondering if there's a technical reason I can't just use a call-by-name parameter to pass lazy reference to the method?
Example:
scala> class MyClass(val apiCall: => String)
<console>:1: error: `val' parameters may not be call-by-name
class MyClass(val apiCall: => String)
Edit I should have specified that my question is why a class can't have a val
parameter. Added an example.
This is because call-by-value functions compute the passed-in expression's value before calling the function, thus the same value is accessed every time. However, call-by-name functions recompute the passed-in expression's value every time it is accessed.
In Scala when arguments pass through call-by-value function it compute the passed-in expression's or arguments value once before calling the function . But a call-by-Name function in Scala calls the expression and recompute the passed-in expression's value every time it get accessed inside the function.
For this circumstance, Scala offers call-by-name parameters. A call-by-name mechanism passes a code block to the call and each time the call accesses the parameter, the code block is executed and the value is calculated.
Call by name is a a parameter passing scheme where the parameter is evaluated when it is used, not when the function is called.
A class can very well have call-by-name parameters, as long as they are not val
or var
:
> scala
Welcome to Scala version 2.11.1 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_55).
Type in expressions to have them evaluated.
Type :help for more information.
scala> class Foo(f: => Unit) {
| def run(): Unit = f
| }
defined class Foo
scala> new Foo(println("hello"))
res0: Foo = Foo@5da6b8c6
scala> res0.run()
hello
The reason a val
or var
parameter cannot be by-name is simply that a class field cannot be by-name, no more than local variable, for that matter. So the following is invalid:
scala> class FooInvalid(val v: => Unit) {
<console>:1: error: `val' parameters may not be call-by-name
class FooInvalid(val v: => Unit) {
^
However, it is possible to have a by-name parameter that is assigned, as a function to a val
field, like this (but then you have to use ()
as call site):
scala> class FooVal(v0: => Unit) {
| val v: () => Unit = () => v0
| }
defined class FooVal
scala> new FooVal(println("hello"))
res2: FooVal = FooVal@75c145bc
scala> res2.v
res3: () => Unit = <function0>
scala> res2.v()
hello
Finally, instead of defining v
as a val
of type () => Unit
, you can define it as def
of type Unit
. And then you get the behavior you probably wanted in the first place:
scala> class FooDef(v0: => Unit) {
| def v: Unit = v0
| }
defined class FooDef
scala> new FooDef(println("hello"))
res5: FooDef = FooDef@2e04a041
scala> res5.v
hello
One could argue that the compiler should do this transformation itself, but that would not be consistent with the semantics that a val
must be stable (e.g., with a stable value you can import x._
its members, which you cannot do with an unstable value) since a def
(even without ()
) is a not a stable value. Best to leave this small rewriting to the user rather than introduce a very weird unsoundness.
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