I'm trying to compile the following code, using Scala 2.11.7.
object LucasSeq {
val fibo: Stream[Int] = 0 #:: 1 #:: fibo.zip(fibo.tail).map { pair =>
pair._1 + pair._2
}
def firstKind(p: Int, q: Int): Stream[Int] = {
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
p * pair._2 - q * pair._1
}
lucas
}
}
fibo is based on the Fibonacci sequence example in Scala's Stream documentation, and it works.
However, the firstKind function, which tries to generalize the sequence with parameters p and q (making Lucas sequences of the first kind), has the following error:
LucasSeq.scala:7: error: forward reference extends over definition of value lucas
val lucas: Stream[Int] = 0 #:: 1 #:: lucas.zip(lucas.tail).map { pair =>
^
one error found
It's basically the same code, so why does it work outside the function but not inside a function?
This error message has puzzled many programmers before me. I've considered…
- So just don't put that code in a function — but I do want a function.
implicit val lucas— doesn't help.- Self-references can only be used in lazy expressions — but this is lazy, right?
- Compile with
-Xprint:typerdiagnostics — not sure what to do with that information. - Is it a shadowing issue? — No, I'm using identifiers that don't clash.
- Compiler bug? — I hope not. The referenced bug should be already fixed in 2.11.7.
I could probably go on reading for hours, but I think it would be best to ask for help at this point. I'm looking for both a solution and an explanation. (I'm familiar with functional programming, but new to Scala, so if the explanation involves terms like "synthetic" and "implicit", then I'll probably need an additional explanation of that as well.)
There was an answer here, but it was deleted for some reason.
There are basically two options. You could make your
valintolazy val. Or you could define yourlucas: Stream[Int]in a class as a field. You can parameterize the class withpandqin the constructor.You are right that the original code is lazy. But it is not lazy enough for scala to translate it.
For the sake of simplicity think into what code
val a = 1 + awill be translated (I know the code does not make sense much). In Javaint a = 1 + awon't work. Java will try to useain1 + a, butais not yet initialized. Even if Java hadInteger a = 1 + a, andawould be a reference, Java still not able to execute this, because Java runs1 + astatement when allocatingaSo it leaves us with two options. Defining
anot as a variable, but as a field. Scala automatically resolve the problem by defining a recursive method, instead of a field - because field in scala is two methods + variable anyway. Or you could tell scala explicitly that it should resolve the lazy problem here by specifying your val aslazy val. This will make scala generate a hidden class with all the necessary infrastructure for it to be lazy.You can check this behavior by running your compiler with
-printoption. The output is rather complicated though, especially inlazy valcase.Also please note that because your stream leaves the scope and also because you have two parameters for your stream -
pandq, your stream will be recomputed each call if you go withlazy valoption. If you choose creating an additional class - you are able to control this, by caching all instances of this class for eachpandqpossibleP.S. By saying
Javahere I of course mean JVM. It just easier to think in terms of Java