Можно ли определить непрерывный поток одного и того же элемента только с помощью алгебры?
Если я посмотрю на реализацию Streams в Scala, я увижу следующее определение метода:
def continually[A](elem: => A): Stream[A] = cons(elem, continually(elem))
Посмотрите на Cons
, вы можете видеть, что это просто кортеж с элементом головы и хвостом.
final class Cons[+A](hd: A, tl: => Stream[A]) extends Stream[A]
У меня трудности с созданием этого Cons
класса только с алгеброй.
def continually[A, G[_]](a: => A): G[A] = {
???
}
Выше не достаточно, потому что мне нужен другой тип, назовем его B
, который представляет Cons
таким образом, что он допускает head
и tail
, но также расширяет себя (Stream
или в этом случае G[_]
).
def continually[A, G[_], B <: G](a: => A): B = {
???
}
Моя первая наивная реализация выглядит следующим образом:
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)
Но это, конечно, не лень.
Моя вторая попытка сделать хвост ленивым, и он работает как положено:
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 ***"))
Что приводит к выводу ниже:
[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
Итак, мой вопрос теперь таков:
Вместо явного запроса implicit pureB: (A, => G[A]) => B
, есть ли другой способ запросить аппликатив, который может создать экземпляр кортежа? Я не могу использовать implicit applicativeB: Applicative[B]
, потому что B не знает, что это должен быть кортеж с ленивым хвостом.
Моя идея состояла в том, чтобы создать новое определение типа для B
, где теперь известно, что это кортеж с ленивым хвостом.
B <: (A, => G[A]): G[A]
Однако теперь это разрешено компилятором:
Error:(77, 39) no by-name parameter type allowed here
def continually[A, G[_], B <: (A, => G[A]): G[A]](a: => A)
Любая идея о том, как я могу создать что-то из B
, которое должно быть кортежем, и в то же время пытаться опираться на вещи, которые уже есть в Cats
или Scalaz
?