如何在给定异步的情况下使用cats-effect的IO生成Traversable

时间:2018-07-20 17:59:26

标签: scala scala-cats cats-effect

我真正想做的是监视多个文件,当其中任何一个被修改时,我想更新一些状态并使用此状态产生副作用。我想我想要的是scan上方的Traversable,它产生一个Traversable[IO[_]]。但我看不到那条路。

作为我写此书的最小尝试

package example

import better.files.{File, FileMonitor}
import cats.implicits._
import com.monovore.decline._
import cats.effect.IO
import java.nio.file.{Files, Path}
import scala.concurrent.ExecutionContext.Implicits.global

object Hello extends CommandApp(
  name = "cats-effects-playground",
  header = "welcome",
  main = {
    val filesOpts = Opts.options[Path]("input", help = "input files") 
    filesOpts.map { files =>
      IO.async[File] { cb  =>
        val watchers = files.map { path =>
          new FileMonitor(path, recursive = false) {
            override def onModify(file: File, count: Int) = cb(Right(file))
          }
        }
        watchers.toList.foreach(_.start)
      }
        .flatMap(f => IO { println(f) })
        .unsafeRunSync
    }
  }
)

但这有两个主要缺陷。它为我正在观看的每个文件创建一个线程,这有点累。但更重要的是,即使修改了一个文件,该程序也会在完成修改后立即完成。即使onModify在程序保持运行的情况下也会被调用多次。

我不喜欢使用更好的文件,这似乎是阻力最小的途径。但是我确实需要使用Cats IO。

1 个答案:

答案 0 :(得分:0)

此解决方案不能解决创建一堆线程的问题,也不能严格产生Traversable,但可以解决基础用例。我很乐意受到批评,并提供了更好的解决方案。

package example

import better.files.{File, FileMonitor}
import cats.implicits._
import com.monovore.decline._
import cats.effect.IO
import java.nio.file.{Files, Path}
import java.util.concurrent.LinkedBlockingQueue
import scala.concurrent.ExecutionContext.Implicits.global

object Hello extends CommandApp(
  name = "cats-effects-playground",
  header = "welcome",
  main = {
    val filesOpts = Opts.options[Path]("input", help = "input files")
    filesOpts.map { files =>
      val bq: LinkedBlockingQueue[IO[File]] = new LinkedBlockingQueue()
      val watchers = files.map { path =>
        new FileMonitor(path, recursive = false) {
          override def onModify(file: File, count: Int) = bq.put(IO(file))
        }
      }
      def ioLoop(): IO[Unit] = bq.take()
          .flatMap(f => IO(println(f)))
          .flatMap(_ => ioLoop())

      watchers.toList.foreach(_.start)
      ioLoop.unsafeRunSync
    }

  }
)