2013-08-18 7 views
2

Я использую scalaz 6.0 с scala. Я использую iteratees для чтения из входного потока.Iteratee input from Standard IO

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

Это
Является
тест

Мой iteratee будет наращивать 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() после первой строки, и я не смог ее разрешить.

ответ

2

Это ошибка в определении 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) 
} 

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