Best Practices for nested Scala Option fields? - scala

I've got a number of nested objects, all wrapped in a Scala Option type. Elsewhere in my project I'm having to call an attribute that is embedded some 5 levels deep (some of which are Lists), each time making a call to .get. This way I end up with something that looks like the following:
objectA.get.attrB.get.attrC.get(0).attrD.get
Other than the series of .get calls (which I'm not sure is ideal), I'm not implementing much error handling this way and should any of the attributes be empty then the whole thing falls apart. Given the nested calls, if I were to limit this to a one-liner as above, I would also only be able to use .getOrElse once at the end.
Are there any suggested ways of working with Option types in Scala?

In this case, the most readable solution out of the box (that is, without writing helper methods) would argueably be to chain calls to Option.flatMap:
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
By using flatMap, if any of the option in the chain is None, you'll end up with None at the end (no exception unlike with get which will blow when called on None).
By example:
case class C(attrD: Option[String])
case class B(attrC: List[C])
case class A(attrB: Option[B])
val objectA = Some(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
val objectA2 = Some(A(Some(B(List()))))
// returns None
objectA2 flatMap (_.attrB) flatMap (_.attrC.headOption) flatMap (_.attrD)
You could also use for comprehensions instead of flatMap (for comprehension being desugared to chains of flatMap/map), but in this case it will actually be less readable (the opposite is usually true) because on each step you'll have to introduce a binding and then reference that on the next step:
for ( a <- objectA; b <- a.attrB; c <- b.attrC.headOption; d <- c.attrD ) yield d
Another solution, if you're willing to use ScalaZ, is to use >>= in place of flatMap, which makes it somewhat shorter:
val objectA = Option(A(Some(B(List(C(Some("foo")), C(Some("bar")))))))
// returns Some(foo)
objectA >>= (_.attrB) >>= (_.attrC.headOption) >>= (_.attrD)
This is really exactly the same as using flatMap, only shorter.

I believe you wanted this,
val deepNestedVal = objectA.get.attrB.get.attrC.get.attrD.get
I think the more preferred way would be to use for-comprehension,
val deepNestedVal = for {
val1 <- objectA
val2 <- val1.attrB
val3 <- val2.attrC
val4 <- val3.attrD
} yield val4
Other way is to use flatMap as shown in the answer by #Régis Jean-Gilles

Related

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.
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)
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.
This returns an Array[Throwable].
for {
(_,_,Failure(e)) <- rows
} yield e
Or, perhaps, an un-sugared version.
rows.collect{case (_,_,Failure(e)) => e}

Scala: reduceLeft with String

I have a list of Integers and I want to make a String of it.
var xs = list(1,2,3,4,5)
(xs foldLeft "") (_+_) // String = 12345
with foldLeft it works perfect, but my question is does it also work with reduceLeft? And if yes, how?
It cannot work this way with reduceLeft. Informally you can view reduceLeft as a special case of foldLeft where the accumulated value is of the same type as the collection's elements. Because in your case the element type is Int and the accumulated value is String, there is no way to use reduceLeft in the way you used foldLeft.
However in this specific case you can simply convert all your Int elements to String up front, and then reduce:
scala> xs.map(_.toString) reduceLeft(_+_)
res5: String = 12345
Note that this will throw an exception if the list is empty. This is another difference with foldLeft, which handles the empty case just fine (because it has an explicit starting value).
This is also less efficient because we create a whole new collection (of strings) just to reduce it on the spot.
All in all, foldLeft is a much better choice here.
It takes a little bit of work to make sure the types are understood correctly. Expanding them, though, you could use something like:
(xs reduceLeft ((a: Any, b: Int) => a + b.toString)).toString

Scala's Either not Having `flatMap` & Meaning of Either.left/.right

Looking at Haskell's Either Monad, there's a >>= function.
Prelude Map> let add100 = \x -> Right (x+100 :: Int)
Prelude Map> x
Right 5
Prelude Map> x >>= add100
Right 105
Prelude Map> let y = Left "..." :: Either String Int
Prelude Map> y >>= add100
Left "..."
However, why doesn't Scala's Either[A,B] have a flatMap, i.e. equivalent to >>= function?
scala> e
res5: Either[String,Int] = Right(1)
scala> e.
asInstanceOf fold isInstanceOf isLeft isRight
joinLeft joinRight left right swap
toString
Also, what's the meaning of left and right?
scala> e.left
res6: scala.util.Either.LeftProjection[String,Int] = LeftProjection(Right(1))
scala> e.right
res7: scala.util.Either.RightProjection[String,Int] = RightProjection(Right(1))
Either can't have a flatMap, because Either is not a monad. Haskell makes it into a monad by favoring one of the two types over the other, but there really is no justification to do so. Either means "this can be one of two types". Period. It does not give any preferential treatment to one of the two types.
If you use Either for error reporting, then it is true that you want it to be biased to one side, but that is only one usecase of many, and hardcoding a single special usecase into a general interface smells of bad design. And for that usecase, you might just as well use Try, which is basically a biased Either.
left and right return a projection of the Either, which is biased to one side.
However, note that the design and usability of Either has been debated very often, and it is indeed not exactly a shining beacon of good API design. Which is one of the reasons why Try and ScalaZ \/ exist.
You can either use fold which requires two functions, one for left and one for right, or use the right projection view:
val e: Either[String, Int] = Right(5)
def add100(i: Int): Either[String, Int] = Right(i + 100)
e.fold(identity, add100) // Right(105)
e.right.flatMap(add100) // Right(105)
So the projection views allow you to see an Either instance as something that will be mapped through the left or right type. This is explained in the Either scala-doc help file.
If you are familiar with Haskell, the ScalaZ library might be for you, it has its own 'either' abstraction called \/ (as described here).
Scala's Either type is unbiased. With types like Try, Future, etc, map and flatMap operate on the successful state, but with Either is this is not the case (though Right is often favored for success). If Either has no true successful state, which value should flatMap operate on? Left or Right? That's left up to you with projections.
left and right are the projections of Either, which are essentially biased versions of Either.
val test: Either[String, Int] = Right(1)
test.right.map(_ + 1) // Right-biased, will map the value to Right(2)
test.left.map(_ + 1) // Left-biased, will not operate on the value since it's `Right`, therefore returning Right(1) again.
Scala’s Either class was redesigned in Scala 2.12. Prior to 2.12, Either was not biased, and didn’t implement map and flatMap methods. As the image from the book shows, Either is redesigned in 2.12 to include those methods, so it can now be used in Scala for-expressions as shown.
https://alvinalexander.com/photos/scala-either-left-right-2.12-biased-map-flatmap/
In Scala 2.12 you can you e.g. this:
for {
right1 <- either1
right2 <- either2
} yield (righ1 + right2)

Flattening a Set of pairs of sets to one pair of sets

I have a for-comprehension with a generator from a Set[MyType]
This MyType has a lazy val variable called factsPair which returns a pair of sets:
(Set[MyFact], Set[MyFact]).
I wish to loop through all of them and unify the facts into one flattened pair (Set[MyFact], Set[MyFact]) as follows, however I am getting No implicit view available ... and not enough arguments for flatten: implicit (asTraversable ... errors. (I am a bit new to Scala so still trying to get used to the errors).
lazy val allFacts =
(for {
mytype <- mytypeList
} yield mytype.factsPair).flatten
What do I need to specify to flatten for this to work?
Scala flatten works on same types. You have a Seq[(Set[MyFact], Set[MyFact])], which can't be flattened.
I would recommend learning the foldLeft function, because it's very general and quite easy to use as soon as you get the hang of it:
lazy val allFacts = myTypeList.foldLeft((Set[MyFact](), Set[MyFact]())) {
case (accumulator, next) =>
val pairs1 = accumulator._1 ++ next.factsPair._1
val pairs2 = accumulator._2 ++ next.factsPair._2
(pairs1, pairs2)
}
The first parameter takes the initial element it will append the other elements to. We start with an empty Tuple[Set[MyFact], Set[MyFact]] initialized like this: (Set[MyFact](), Set[MyFact]()).
Next we have to specify the function that takes the accumulator and appends the next element to it and returns with the new accumulator that has the next element in it. Because of all the tuples, it doesn't look nice, but works.
You won't be able to use flatten for this, because flatten on a collection returns a collection, and a tuple is not a collection.
You can, of course, just split, flatten, and join again:
val pairs = for {
mytype <- mytypeList
} yield mytype.factsPair
val (first, second) = pairs.unzip
val allFacts = (first.flatten, second.flatten)
A tuple isn't traverable, so you can't flatten over it. You need to return something that can be iterated over, like a List, for example:
List((1,2), (3,4)).flatten // bad
List(List(1,2), List(3,4)).flatten // good
I'd like to offer a more algebraic view. What you have here can be nicely solved using monoids. For each monoid there is a zero element and an operation to combine two elements into one.
In this case, sets for a monoid: the zero element is an empty set and the operation is a union. And if we have two monoids, their Cartesian product is also a monoid, where the operations are defined pairwise (see examples on Wikipedia).
Scalaz defines monoids for sets as well as tuples, so we don't need to do anything there. We'll just need a helper function that combines multiple monoid elements into one, which is implemented easily using folding:
def msum[A](ps: Iterable[A])(implicit m: Monoid[A]): A =
ps.foldLeft(m.zero)(m.append(_, _))
(perhaps there already is such a function in Scala, I didn't find it). Using msum we can easily define
def pairs(ps: Iterable[MyType]): (Set[MyFact], Set[MyFact]) =
msum(ps.map(_.factsPair))
using Scalaz's implicit monoids for tuples and sets.

Is there a simple way of defaulting out of bounds in nested Seqs in Scala?

I've got a Vector of Vectors that I'm accessing to apply a boolean function. i.e.
Vector[Vector[T]] where I'm going to execute something along the lines of
f(myVector(i)(j)) where f is of type T => Boolean.
But this does not do bounds checking, and I can't get something really elegant.
I can use applyOrElse:
myVector.applyOrElse(i, (_:Int) => Vector.empty).applyOrElse (j, (_:Int) => defaultT)
where f(defaultT) would return false
But I wish I could just set a default Value instead of a function.
I could use lift to give me an Option, but it doesn't compose well at the second level:
myVector.lift(i) map (_.lift(j) map f getOrElse false) getOrElse false
Which does work, but is still really hard to read.
And then there's standard if/else blocks:
if (myVector.size <= i) false
else {
val myVector2 = levelVector(i)
if (myVector2.size <= j) false
else f(myVector2(j))
}
It just seems like something that should be able to be decomposed easier than what I can achieve. And if I add a 3rd layer, it's gets even uglier.
Are there other options?
Disclaimer: this is adapted from coursera's progfun course
Take the following code from the question:
myVector.lift(i) map (_.lift(j) map f getOrElse false) getOrElse false
This can be rewritten as follows:
myVector.lift(i).flatMap(_.lift(j)).fold(false)(f)
Or, before fold was introduced in Scala 2.10:
myVector.lift(i).flatMap(_.lift(j)).map(f).getOrElse(false)
The key idea is to defer unwrapping (or mapping) the Option for as long as possible. This approach will generalize quite naturally to more than two dimensions.
This is pretty close to equivalent to the for-comprehension in your answer (assuming you meant to include lift in there), but once you have to wrap the comprehension in parentheses I personally tend to find the desugared version clearer.
I've figured something that seems elegant, even if it seems to be a bit overkill:
(for {
myVector2 <- myVector(i)
t <- myVector2(j)
} yield t) map f getOrElse false
Is this sensible? It's certainly readable. Is it slow?
Assuming you vector is named vec and your predicate is named pred, this expression will yield right value: vec.lift(p.row).fold(false)(_.lift(p.col).fold(false)(pred(_))