Итеративный ввод от Standard IO

Я использую скалаз 6.0 со скалой. Я использую итераторы для чтения из входного потока.

Вот простой файл с именем simple.txt, который у меня есть.

это
Является
Тест

Мой итератор создаст монаду io для печати строк

def printLines: IterV[String, IO[Unit]] = {
  def step(currentIO: IO[Unit])(input: Input[String]): IterV[String, IO[Unit]] = 
    input match {
      case El(x) => Cont(
          step(
              currentIO.flatMap(_ => putStrLn(x))
          )
      )
      case IterV.Empty() => Cont(step(currentIO))
      case EOF() => Done(currentIO, EOF[String])
    }
  Cont(step(io()))
}

Когда я использую enumeratorM

getFileLines(new File(".../simple.txt"))(printLines).flatMap(_.run).unsafePerformIO

Я получаю правильный вывод.

Когда я пытаюсь использовать

getLines(printLines).flatMap(_.run).unsafePerformIO

Я только получаю "Это" обратно на консоль. getLines использует стандартный поток ввода. Я добавил операторы отладки в iteratee, и getLines, похоже, отправляют EOF() после первой строки, и я не смог ее разрешить.

1 ответ

Решение

Это ошибка в определении getReaderLines, Сравните текущую версию:

/** Enumerate the lines from a BufferedReader */
def getReaderLines(r: => BufferedReader): EnumeratorM[IO, String] =
  new EnumeratorM[IO, String] {
    def apply[A](it: IterV[String, A]) = {
      def loop(i: IterV[String, A]): IO[IterV[String, A]] = i.fold(
        done = (_,_) => io { i },
        cont = k => for {
          s <- rReadLn(r)
          a <- s.map(l => loop(k(El(l)))).getOrElse(io(i))
        } yield a
      )
      loop(it)
    }
  }

С тем, который работает:

/** Enumerate the lines from a BufferedReader */
def getReaderLines(r: => BufferedReader): EnumeratorM[IO, String] =
  new EnumeratorM[IO, String] {
    lazy val reader = r

    def apply[A](it: IterV[String, A]) = {
      def loop(i: IterV[String, A]): IO[IterV[String, A]] = i.fold(
        done = (_,_) => io { i },
        cont = k => for {
          s <- rReadLn(reader)
          a <- s.map(l => loop(k(El(l)))).getOrElse(io(i))
        } yield a
      )
      loop(it)
    }
  }

Проблема в том, что r является параметром по имени, что означает, что при условии, что getLines определяется, текущая версия создает новый читатель, заключающий в себе стандартный ввод для каждого цикла.

Пока это не будет исправлено в библиотеке (и я сомневаюсь, что будет много спешки с выпуском 6.0.5 за дверь), самое простое решение - написать свой собственный. getLines:

val getLines: EnumeratorM[IO, String] = {
  val r = new BufferedReader(new InputStreamReader(System.in))
  getReaderLines(r)
}

Это будет работать как ожидалось.

Другие вопросы по тегам