Hãy xem xét mã này (được lấy từ here và được sửa đổi để sử dụng byte thay vì dòng ký tự).Làm thế nào để sử dụng IO với Scalaz7 Iteratees mà không làm tràn ngăn xếp?
import java.io.{ File, InputStream, BufferedInputStream, FileInputStream }
import scalaz._, Scalaz._, effect._, iteratee.{ Iteratee => I, _ }
import std.list._
object IterateeIOExample {
type ErrorOr[+A] = EitherT[IO, Throwable, A]
def openStream(f: File) = IO(new BufferedInputStream(new FileInputStream(f)))
def readByte(s: InputStream) = IO(Some(s.read()).filter(_ != -1))
def closeStream(s: InputStream) = IO(s.close())
def tryIO[A, B](action: IO[B]) = I.iterateeT[A, ErrorOr, B] {
EitherT(action.catchLeft).map(r => I.sdone(r, I.emptyInput))
}
def enumBuffered(r: => BufferedInputStream) = new EnumeratorT[Int, ErrorOr] {
lazy val reader = r
def apply[A] = (s: StepT[Int, ErrorOr, A]) => s.mapCont(k =>
tryIO(readByte(reader)) flatMap {
case None => s.pointI
case Some(byte) => k(I.elInput(byte)) >>== apply[A]
})
}
def enumFile(f: File) = new EnumeratorT[Int, ErrorOr] {
def apply[A] = (s: StepT[Int, ErrorOr, A]) =>
tryIO(openStream(f)).flatMap(stream => I.iterateeT[Int, ErrorOr, A](
EitherT(
enumBuffered(stream).apply(s).value.run.ensuring(closeStream(stream)))))
}
def main(args: Array[String]) {
val action = (
I.consume[Int, ErrorOr, List] &=
enumFile(new File(args(0)))).run.run
println(action.unsafePerformIO())
}
}
Chạy mã này trên tệp có kích thước phong nha (8kb) tạo ra StackOverflowException. Một số tìm kiếm bật lên rằng các ngoại lệ có thể tránh được bằng cách sử dụng các Trampoline monad thay vì IO, nhưng điều đó không có vẻ giống như một giải pháp tuyệt vời - hy sinh chức năng tinh khiết để có được chương trình để hoàn thành ở tất cả. Cách rõ ràng để sửa lỗi này là sử dụng IO hoặc Trampoline như một biến thể Monad để bọc khác, nhưng tôi không thể tìm thấy một thực hiện của phiên bản biến áp của một trong số họ và tôi không đủ của một guru chức năng lập trình để biết làm thế nào để viết của riêng tôi (tìm hiểu thêm về FP là một trong những mục đích của dự án này, nhưng tôi nghi ngờ việc tạo ra các biến thế đơn nguyên mới cao hơn một chút so với mức hiện tại của tôi). Tôi cho rằng tôi chỉ có thể quấn một hành động IO lớn xung quanh việc tạo, chạy và trả về kết quả của các iterate của tôi, nhưng điều đó cảm thấy giống như một giải pháp thay thế một giải pháp.
Có lẽ một số monads không thể chuyển đổi thành máy biến áp đơn, vì vậy tôi muốn biết nếu nó có thể làm việc với các tệp lớn mà không làm giảm IO hoặc tràn ngăn xếp, và nếu như vậy, làm thế nào?
Câu hỏi bổ sung: Tôi không thể nghĩ ra bất kỳ cách nào để một iteratee báo hiệu rằng nó gặp phải lỗi trong khi xử lý ngoại trừ việc trả về một trong hai điều này, điều này khiến việc tạo chúng trở nên dễ dàng hơn. Đoạn mã trên cho thấy cách sử dụng EitherT để xử lý các lỗi trong điều tra viên, nhưng nó hoạt động như thế nào cho các iterate?
Điều này có thể hữu ích cho bạn: http://termsandtruthconditions.herokuapp.com/blog/2013/03/16/free-monad/ – Impredicative
Đó là một giải thích tốt về lý do tại sao tôi cần sử dụng tấm bạt lò xo để tránh làm tràn ngăn xếp, nhưng nó không bao gồm cách sử dụng cả IO và Trampoline. – Redattack34
IO bị trampolined rồi. – Apocalisp