2011-10-03 18 views
6

我想使用IO monad。Scalaz的遍歷_IO monad

但是這段代碼不能用大文件運行。 我得到一個StackOverflowError。 我試過-DXss選項,但它會拋出相同的錯誤。

val main = for { 
    l <- getFileLines(file)(collect[String, List]).map(_.run) 
    _ <- l.traverse_(putStrLn) 
} yield() 

我該怎麼辦?


我寫了輸出所有元素的Iteratee。

def putStrLn[E: Show]: IterV[E, IO[Unit]] = { 
    import IterV._ 
    def step(i: IO[Unit])(input: Input[E]): IterV[E, IO[Unit]] = 
    input(el = e => Cont(step(i >|> effects.putStrLn(e.shows))), 
     empty = Cont(step(i)), 
      eof = Done(i, EOF[E])) 
    Cont(step(mzero[IO[Unit]])) 
} 
val main = for { 
    i <- getFileLines(file)(putStrLn).map(_.run) 
} yield i.unsafePerformIO 

這也是相同的結果。

我認爲是由IO實現引起的。

+1

的第一個問題是,爲什麼*/*如何豈不是有一個大的文件運行你得到一個堆棧溢出錯誤,內存不足錯誤或其他內容? –

+1

我得到一個StackOverflowError。我嘗試了-DXss選項,但是拋出了相同的錯誤。 –

+0

同意,我認爲IO monad增加了一些挑戰。 – huynhjl

回答

4

這是因爲scalac未針對尾部調用優化loop內部getReaderLinesloop是尾遞歸,但我認爲case匿名函數語法阻礙。

編輯:實際上它甚至不是尾遞歸(IO monad中的包裝)在遞歸調用後至少會導致一個以上的調用。當我昨天正在進行測試時,我使用了類似的代碼,但是我放棄了IO monad,然後可以使Iteratee尾部遞歸。下面的文本,假設沒有IO monad ...

我碰巧發現昨天,而試驗iteratees。我想改變的loop的簽名,這將有助於(所以暫且你可能要重新實現getFilesLinesgetReaderLines

@annotations.tailrec 
def loop(it: IterV[String, A]): IO[IterV[String, A]] = it match { 
    // ... 
} 

我們或許應該報告給scalaz民間(也可以是開放增強票。斯卡拉)

這說明發生了什麼(代碼隱約類似getReaderLines.loop):

@annotation.tailrec 
def f(i: Int): Int = i match { 
    case 0 => 0 
    case x => f(x - 1) 
} 
// f: (i: Int)Int 

@annotation.tailrec 
def g: Int => Int = { 
    case 0 => 0 
    case x => g(x - 1) 
} 
/* error: could not optimize @tailrec annotated method g: 
it contains a recursive call not in tail position 
     def g: Int => Int = { 
         ^
*/ 
+0

請做報告,這樣可以改進! – AndreasScheinert