0

filter (which uses halt inside) terminates other branch even if it has some side-effects:

scala> val p = Process("1","2", "3")
scala> val p1 = p.filter(_ => true).map(_ + "p1").observe(io.stdOutLines)
scala> val p2 = p.filter(_ => false).map(_ + "p2").observe(io.stdOutLines)
scala> (p1 yip p2).run.run
1p1

scala> val p2 = p.filter(_ => true).map(_ + "p1").observe(io.stdOutLines)
scala> (p1 yip p2).run.run
1p1
1p2
2p1
2p2
3p1
3p2

Seems logical as there is no value to be returned to yip after that filter. But what about side-effects, specified with observe?

My current solution is to use flatMap to specify default value:

scala> val p1 = p.map(_ + "p1").flatMap(x => Process.emit(x).observe(io.stdOutLines))

scala> val p2 = p.map(_ + "p2").flatMap(x => Process.emit(""))

scala> (p1 yip p2).run.run
1p1
2p1
3p1

But maybe there is a way to use filter?

P.S. merge combinator executes side-effects for other branch (as it doesn't require value to be returned), but it doesn't wait for other branch if one halts (even if it has side-effects).

dk14
  • 22,206
  • 4
  • 51
  • 88

2 Answers2

0

To run the effects even after p2 terminates there needs to be clear default behaviour. So there are probably these solutions:

  1. define p2 to supply default value after being terminated
  2. use either wye to get left and rights if we don't really need tuples

perhaps the (1) is closer to question and code will looks like:

val p = Process("1","2", "3")
val p1 = p.filter(_ => true).map(_ + "p1").observe(io.stdOutLines)
val p2 = p.filter(_ => false).map(_ + "p2")
         .observe(io.stdOutLines).map(Some(_)) ++ emit(None).repeat
// alternativelly
// val p2 = p.map { v =>  if (pred(v)) right(v) else left(v) }
//          .observeO(o.stdOutLines).flatMap { _.toOption }
//          ++ emit(None).repeat             

(p1 yip p2).run.run
Pavel Chlupacek
  • 864
  • 5
  • 8
  • I believe `either` wye has the same problem as `merge` it will execute side-effects, but won't wait for them. Unfortunatelly same for the first option, just try `val p2 = p.map{x => Thread.sleep(1000);println("!" + x);x}.filter(_ => false).map(_ + "p2").observe(io.stdOutLines).map(Some(_)) ++ emit(None).repeat` and you'll see races between "2" and "3" messages. That's the reason I can't use `merge` combinator – dk14 Nov 11 '15 at 13:32
  • My process is actually getting messages from one queue and publishing them to another one. And in both branches - it's the same destination queue, so I have to maintain correct order. Anyway, thanks for the answer. – dk14 Nov 11 '15 at 13:35
  • Why you just cannot do then `q.dequeue.pipe(processor).to(q.enqueue)` ? – Pavel Chlupacek Nov 12 '15 at 09:33
  • I have `.observe(q.enqueue)` in both branches instead of `observe(io.stdOutLines)`. Anyway I'm pretty happy with my `flatMap`, no big deal – dk14 Nov 12 '15 at 19:56
0

Actually it should be just something like that:

in.map(emit).flatMap{ p =>
  val p1 = p.map(_ + "p1").filter(_ => true).observe(out)
  val p2 = p.map(_ + "p2").filter(_ => false).observe(out)
  p1 merge p2
}.run.run

It makes all side effects being in order as filter can't get more than one value (produced by emit)

dk14
  • 22,206
  • 4
  • 51
  • 88