Would it be possible to define a continuous stream of the same element with just algebra?
If I look at the implementation of Streams in Scala I see the following method definition:
def continually[A](elem: => A): Stream[A] = cons(elem, continually(elem))
Look at Cons
you can see that it's just a tuple with a head element and a tail.
final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]
I'm having difficulties of creating this Cons
class with just algebra.
def continually[A, G[_]](a: => A): G[A] = {
???
}
The above is not enough, because I need a different type, let's call it B
that represents Cons
in a way that it allows a head
and a tail
, but that also extends itself (Stream
or in this case G[_]
).
def continually[A, G[_], B <: G](a: => A): B = {
???
}
My first naive implementation is as follows:
def continually[A, G[_], B <: G[A]](a: => A)
(implicit pureB: (A, G[A]) => B): B = {
pureB(a, continually[A, G, B](a))
}
// Run
implicit def pureStream[A](head: A, tail: Stream[A]) = cons(head, tail)
continually[Int, Stream, Cons[Int]](1)
But this is of course not lazy.
My second attempt is making the tail lazy and it works as expected:
def continually[A, G[_], B <: G[A]](a: => A)
(implicit pureB: (A, => G[A]) => B): B = {
println("[continually called]")
pureB(a, continually[A, G, B](a))
}
implicit def pureStream[A](head: A, tail: => Stream[A]) = cons(head, tail)
val result = Source
.fromIterator(() => continually[Int, Stream, Cons[Int]](1).toIterator)
.throttle(1, 1.second)
.runWith(Sink.foreach(println))
result.onComplete(_ => println("*** Finished ***"))
Which results in the output below:
[continually called]
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
[continually called]
1
So my question now is as follows:
Instead of explicitly asking for a implicit pureB: (A, => G[A]) => B
, is there any other way to ask for an applicative that can instantiate a tuple? I can't use implicit applicativeB: Applicative[B]
, because B is not aware that is supposed to be a tuple with a lazy tail.
My idea was to create a new type definition for B
where it is now aware that it is a tuple with a lazy tail.
B <: (A, => G[A]): G[A]
However this is now allowed by the compiler:
Error:(77, 39) no by-name parameter type allowed here
def continually[A, G[_], B <: (A, => G[A]): G[A]](a: => A)
Any idea on how I can create something of B
that is supposed to be a tuple and at the same time trying to lean on stuff that is already there in Cats
or Scalaz
?