简体   繁体   中英

Why can't a class val parameter be call-by-name?

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.

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 (eg, 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM