18

I observed a difference in Scala's type inference when applied to def and val.

Using def, I can define an abstract nullary method const returning some value of type Int => Int. When implementing const with a function literal, I need not supply a parameter type, as it can be inferred by the compiler:

trait D {
  def const: Int => Int
}
object D extends D {
  def const = i => i + 1
}

This is fine. (On the downside, a new function instance is being created for every access to D.const.)

Now consider an analogous construction using val:

trait V {
  val const: Int => Int
}
object V extends V {
  val const = i => i + 1
}

This will not compile, failing with

error: missing parameter type
   val const = i => i + 1
               ^

Why?

knuton
  • 3,537
  • 3
  • 24
  • 23
  • 1
    It is not really a safe idea to use abstract `val`'s in traits (can lead to surprising NPEs). Keep it a `def` in the trait and override it with a `val` in the implementation. Or begin with a `lazy val x: X = sys.error("override me")` in the trait. – ron May 24 '12 at 15:48
  • Could it have anything to do with pattern matching? Everything between the `def` and the equals sign `=` is treated as an identifier, whereas everything between the `val` and the equals sign `=` is treated as a pattern. Just a wild guess... – agilesteel May 24 '12 at 15:55
  • @ron, thanks for mentioning it, but one way or another the question remains, as using a `def` in the trait and a `val` in the implementation leads to the same issue. – knuton May 24 '12 at 17:15
  • @agilesteel, at first I thought you might be on to something, after finding an issue in the Scala tracker (https://issues.scala-lang.org/browse/SI-785). But if my understanding is correct, only certain identifiers are stable identifiers, see here: http://stackoverflow.com/questions/7078022/why-does-pattern-matching-in-scala-not-work-with-variables – knuton May 24 '12 at 17:19

2 Answers2

2

If you build this code with the option -Xprint all , you will see that :

abstract trait V extends scala.AnyRef {   
<stable> <accessor> def const: Int => Int
};

final object V extends java.lang.Object with V with ScalaObject {

def this(): object V = {
  V.super.this();
  ()
};

private[this] val const: <error> => <error> = ((i: <error>) => i.+(1));
<stable> <accessor> def const: <error> => <error> = V.this.const
}

So the error occurs at the creation of private val and accessor. th compilator try to evaluate the value affected to the val const before creating the accessor def const.

if you look to the val const defined in trait, you see that the creation of private val was disabled because it's only a definition for the def const accessor.

I think the inference type with previous definition ( in trait or superclass) occurred only when he try to create the accessor, not for evaluate a value.

And for the last def const , the type is only based on the private[this] val const type : error => error

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
fp4me
  • 463
  • 2
  • 8
  • Yes, that must be it! Can you judge whether this is according to spec or simply a bug in the compiler? – knuton May 25 '12 at 10:57
  • I am not considering that like a bug, i think, in this case, you have no reason to use a val because the value is defined only at compilation time, not at execution time ( there isn't any reference in the value to a parameter or a variable previously define , it's just a definition of an anonymous function. But may be Martin Odersky will see this post and give us more explanations ;) – fp4me May 25 '12 at 13:22
  • Well, this is only a toy example. In the code I derived it from, the body of the anonymous function does use a name from the enclosing scope. I think the fact that this specific instance can be solved differently, shoudn't let the compiler off the hook. – knuton May 25 '12 at 13:56
1

As of Scala 2.9.1, this is “as speced”. Citing Martin Odersky from SI-2742:

For methods, return types in inherited abstract methods are taken as expected type of the right hand side. For values there is no such rule. So this would be a spec enhancement request, as I see it.

The ticket is low priority and has remained unchanged since its first discussion in late 2009, so it seems unlikely to change anytime soon.

knuton
  • 3,537
  • 3
  • 24
  • 23