简体   繁体   English

如何将进程发送到scalaz-stream中的多个接收器

[英]How to send a process to multiple sinks in scalaz-stream

If I have a simple process which is emitting values of type String and I wish to send these to multiple sinks (ie each sink gets sent the String ), how do I do this? 如果我有一个简单的过程正在发出String类型的值,并且希望将它们发送到多个接收器(即,每个接收器都发送给String ),我该怎么做?

For example, running this program: 例如,运行此程序:

object Play extends App {

  def prepend(s: String): String => String = s ++ _
  val out1 = io.stdOutLines.map(prepend("1-") andThen _)
  val out2 = io.stdOutLines.map(prepend("2-") andThen _)

  val p = io.stdInLines to (out1 merge out2)
  p.run.run
}

The output looks like: 输出如下:

a     //input
1-a
b     //input
2-b
c     //input
2-c
d     //input
1-d

I want the output to be this: 我希望输出是这样的:

a     //input
1-a
2-a
b     //input
2-b
1-b
c     //input
2-c
1-c
d     //input
1-d
2-d

EDIT 编辑

I can achieve this as follows: 我可以达到以下目的:

implicit class ToBoth[O](p: Process[Task, O]) {
  def toBoth(s1: Sink[Task, O], s2: Sink[Task, O]): Process[Task, Unit] = {
    (for (o <- p; n <- Process.emit(o) ++ Process.emit(o)) yield n) to (s1 interleave s2)
  }
}

That is, I duplicate the input and interleave the output. 也就是说,我复制输入并交错输出。 This can be generalized: 可以概括为:

def toAll(sinks: Sink[Task, O] *): Process[Task, Unit] = {
  (for (o <- p; n <- Process.emitAll(sinks.map(_ => o))) yield n) to sinks.reduceLeftOption(_ interleave _).getOrElse(Process.empty)
}

EDIT 2 编辑2

I just realized that the generalization toAll does not work. 我只是意识到对toAll的泛化不起作用。 toBoth does, though toBoth都可以

Is there a better (built-in) way? 有更好的(内置)方法吗?

You could also use observe and to to attach multiple Sink s to a Process : 你也可以使用observe ,并to多种附加Sink s到一个Process

val p = io.stdInLines.observe(out1).to(out2)

observe is like to but echoes what is passed into the sink. observe就像to ,但相呼应,什么是传递到下沉。 So io.stdInLines.observe(out1) still emits the strings that come from stdin (that means it is of type Process[Task, String] ) but also sends them to the sink out1 . 因此io.stdInLines.observe(out1)仍然会发出来自stdin的字符串(这意味着它的类型为Process[Task, String] ),而且还会将它们发送到接收器out1


As Eric pointed out, it is also possible to zip Sink s together. 正如Eric指出的那样,也可以将Sink zip在一起。 Here is a more elaborate example that sends individual lines of a logfile to different sinks depending on their log level: 这是一个更详细的示例,根据日志级别将日志文件的各行发送到不同的接收器:

sealed trait Loglevel
case object Info extends Loglevel
case object Debug extends Loglevel
case object Warning extends Loglevel

case class Line(level: Loglevel, line: String)

val outInfo = io.stdOutLines.contramap((l: Line) => "I: " + l.line)
val outDebug = io.stdOutLines.contramap((l: Line) => "D: " + l.line)
val outWarning = io.stdOutLines.contramap((l: Line) => "W: " + l.line)

val zipped = outInfo.zip(outDebug).zip(outWarning).map {
  case ((fInfo, fDebug), fWarning) =>
    (l: Line) => l.level match {
      case Info    => fInfo(l)
      case Debug   => fDebug(l)
      case Warning => fWarning(l)
    }
}

val lines = List(
  Line(Info, "Hello"),
  Line(Warning, "Oops"),
  Line(Debug, "ui ui"),
  Line(Info, "World"))

Process.emitAll(lines).liftIO.to(zipped).run.run

Running this will output: 运行此命令将输出:

I: Hello
W: Oops
D: ui ui
I: World

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM