buggaby
buggaby

Reputation: 441

Scala filter by nested Option/Try monads

In Scala, I have an Array[Option[(String,String,Try[String])]] and would like to find all the Failure error codes.

If the inner monad is an Option[String] instead, I can access the Some(x) contents with a clean little for comprehension, like so:

for {
  Some(row) <- row 
  (a,b,c) = row 
  x <- c
} yield x

But if the inner monad is a Failure, then I'm struggling to see how to pattern match it, since I can't put Failure(x) <- c in the for statement. This feels like a really simple thing I'm missing, but any guidance would be very valuable.

Many thanks!

EDIT - Mis-specified the array. It's actually an array of option-tuple3s, not just tuple3s.

Upvotes: 2

Views: 679

Answers (3)

mfirry
mfirry

Reputation: 3692

Will a.map(_._3).filter(_.isFailure) do?

EDIT: after having seen the edit and your comment, I think you can also do

val tries = for {
    x <- a
    z <- x
} yield z._3

tries.filter(_.isFailure)

Upvotes: 4

jwvh
jwvh

Reputation: 51271

This returns an Array[Throwable].

for {
  (_,_,Failure(e)) <- rows
} yield e

Or, perhaps, an un-sugared version.

rows.collect{case (_,_,Failure(e)) => e}

Upvotes: 2

Andrei T.
Andrei T.

Reputation: 2480

In order to combine different types of "monads" you will need what's called a monad transformer. To put it simply, Scala doesn't let you mixin different monad types within the same for comprehension - this makes sense since a for comprehension just syntactic sugar for combinations of map / flatMap / filter.

Assuming the first one is always an Option then you could transform the Try into an Option and get the desired result:

for {
  Some((a, b, c)) <- row
  x               <- c.toOption
} yield x

If you don't really care about what's inside that Try that's fine, but if you do then be careful that you'll lose that information when doing Some(x). If the pattern match fails, then you will get a None.

I hope that helps you.

Upvotes: 3

Related Questions