An infinite stream:
val ones: Stream[Int] = Stream.cons(1, ones)
How is it possible for a value to be used in its own declaration? It seems this should produce a compiler error, yet it works.
An infinite stream:
val ones: Stream[Int] = Stream.cons(1, ones)
How is it possible for a value to be used in its own declaration? It seems this should produce a compiler error, yet it works.
The reason this does not produce a compiler error is because both
Stream.cons
andCons
are non-strict and lazily evaluate their second parameter.ones
can be used in it's own definition because the object cons has an apply method defined like this:And Cons is defined like this:
Notice that it's second parameter
tl
is passed by name (=> Stream[A]
) rather than by value. In other words, the parametertl
is not evaluated until it is used in the function.One advantage to using this technique is that you can compose complex expressions that may be only partially evaluated.
It's not always a recursive definition. This actually works and produces 1:
variable
a
is created when you typeval a: Int
, so you can use it in the definition.Int
is initialized to 0 by default. A class will be null.As @Chris pointed out, Stream accepts
=> Stream[A]
so a bit another rules are applied, but I wanted to explain general case. The idea is still the same, but the variable is passed by-name, so this makes the computation recursive. Given that it is passed by name, it is executed lazily. Stream computes each element one-by-one, so it callsones
each time it needs next element, resulting in the same element being produces once again. This works:Though you can make infinite stream easier:
Stream.continually(1)
Update As @SethTisue pointed out in the commentsStream.continually
andStream.cons
are two completely different approaches, with very different results, becausecons
takesA
whencontinually
takes=>A
, which means thatcontinually
recomputes each time the element and stores it in the memory, whencons
can avoid storing it n times unless you convert it to the other structure likeList
. You should usecontinually
only if you need to generate different values. See @SethTisue comment for details and examples.But notice that you are required to specify the type, the same as with recursive functions.
And you can make the first example recursive:
This will stackoverflow.
Look at the signature of Stream.cons.apply:
The
⇒
on the second parameter indicates that it has call-by-name semantics. Therefore your expressionStream.cons(1, ones)
is not strictly evaluated; the argumentones
does not need to be computed prior to being passed as an argument fortl
.