Непрерывно стримись только с алгеброй в Scala - PullRequest
0 голосов
/ 13 января 2019

Можно ли определить непрерывный поток одного и того же элемента только с помощью алгебры?

Если я посмотрю на реализацию 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?

...