Reputation: 14803
I wanted to add an answer to abort-early-in-a-fold for ZIO.
So I took the solution with cats: cats solution
def sumEvenNumbers(nums: Stream[Int]): Option[Long] = {
import cats.implicits._
nums.foldM(0L) {
case (acc, c) if c % 2 == 0 => Some(acc + c)
case _ => None
}
}
How can this be achieved with ZIO?
The closest I got:
new DefaultRuntime {}
.unsafeRun(sumEvenNumbers(List(2,4,6,3,5,6)))
def sumEvenNumbers(nums: Iterable[Int]): ZIO[Any, Nothing, Int] = {
stream.Stream.fromIterable(nums)
.run(Sink.fold(0)(s => s % 2 == 0) { (a: Int, b: Int) => (a + b, Chunk.empty)
})
}
But that gives me: 15
instead of 12
. So it seems to short circuit but it takes a number too many. And it is an Int
not Option[Int]
.
Upvotes: 3
Views: 1052
Reputation: 849
A solution without zio.stream.Stream
:
def sumEvenNumbers(as: Iterable[Int]): UIO[Option[Int]] =
ZIO
.foldLeft(as)(0)((s, a) => if (a % 2 == 0) ZIO.succeed(s + a) else ZIO.fail(s))
.option
.foldLeft
- as soon as a number is not even - the fold fails..option
to merge the Error channel to the Success channel to an Option
.Upvotes: 2
Reputation: 14803
With the idea of @Yuval Itzchakov and my remark that Option
is to express an Exception (None
).
I came up with this solution:
def sumEvenNumbers(nums: Iterable[Int]): UIO[Option[Int]] = {
Stream.fromIterable(nums)
.run(Sink.foldLeftM(0) { (acc: Int, b: Int) =>
if (b % 2 == 0)
ZIO.succeed(acc + b)
else
ZIO.fail(acc)
}).fold(
_ => None,
v => Some(v)
)
}
foldLeftM
- as soon as a number is not even - the fold fails.Option
Upvotes: 1
Reputation: 149598
One option is to takeWhile
and then fold
:
import zio._
import zio.stream._
object Foo extends zio.App {
override def run(args: List[String]): ZIO[zio.ZEnv, Nothing, Int] =
Stream
.fromIterable(List(2, 4, 6, 3, 5, 6))
.takeWhile(_ % 2 == 0)
.fold(0L)(_ + _)
// Just to print the output before terminating
.flatMap(res => zio.console.putStrLn(res.toString) *> ZIO.succeed(0))
}
I don't see a reason for this to return an Option[Long]
.
Upvotes: 1