Sebastian Oberhoff
Sebastian Oberhoff

Reputation: 1301

How do I concisely check for nulls in a long method chain in Scala?

I'm in the situation that I receive a tree data structure from outside, which can be of several different shapes. I need to do a sorta switch case depending on what kind of tree I get. The code I'm writing is beginning to look like this:

val first = Option(root.getA)
    .flatMap(o => Option(o.getB))
    .flatMap(o => Option(o.getC))
...
val second = Option(root.getA)
    .flatMap(o => Option(o.getD))
    .flatMap(o => Option(o.getE))
...
first.getOrElse(second.getOrElse...

'first' checks if the tree has the shape root->A->B->C..., 'second' checks if the tree has the shape root->A->D->E... etc. I feel like there ought to be a simpler way to express this idea in code, since all this code is doing is checking for null at every step by wrapping and unwrapping Options, but I can't find it.

Upvotes: 0

Views: 160

Answers (3)

Simon
Simon

Reputation: 6353

You can do this

case class Node(val a: Node, val b: Node, val c: Node, val data: Int)

node match {
    case Node(a: Node, b: Node, c: Node, _) => ...
    case Node(null, b: Node, c: Node, _) => ...
    case Node(a: Node, null, c: Node, _) => ...
    case Node(a: Node, b: Node, null, _) => ...
    case _ => ...
}

Upvotes: 0

pndc
pndc

Reputation: 3795

If getA etc are placeholders for something with parameters like getValue("A"), you can write a simple function that took root and ("A", "B", "C") and easily walk the tree checking for nulls along the way. I assume you've asked the question because it's not this simple. However you could parameterise on method calls using reflection.

Another possibility if the tree is relatively small or your Scala code is performing the majority of the work on it is to recursively copy and transform the tree into a more Scala-like structure that is easier to process. If you are feeling particularly twisted, you could transform it into an XML document and then use pattern-match using XML primitives.

Alternatively, you could write custom extractors if there are relatively few different getX functions:

object GotA {
  def unapply(x: Thing) = Option(x) map {_.getA}
}

Then your code becomes simple pattern-matching like this:

root match {
   case GotA(GotB(GotC(x))) => x
   case GotA(GotD(GotE(x))) => x
}

If the node-finding rules are so irregular that it's not amenable to any of these approaches, you can at least use a for comprehension that's the equivalent code to what you gave, which you may find more aesthetically pleasing:

val first = for {
  a <- Option(root.getA)
  b <- Option(a.getB)
  c <- Option(b.getC)
} yield c

Sadly, because you've heavily anonymised the question and don't indicate how many of these lookups would be required or how complex they are, I can't recommend a specific solution.

Upvotes: 2

Archeg
Archeg

Reputation: 8462

You could do:

val first = Try(root.getA.getB.getC).toOption

But this can be slow, and not recommended. Much better way to make your getN methods return Option, and use them as you originally intended, but with for-comprehension:

val first = for {
       a <- root.getA
       b <- a.getB
       c <- b.getC
       ...

Upvotes: 1

Related Questions