Best practices: workin with Java: handle nested null checks- getA().getB().getC()....

910 views
Skip to first unread message

Piotr Kopeć

unread,
Feb 7, 2013, 3:50:54 AM2/7/13
to scala-l...@googlegroups.com
Hi All,
I want to ask you about best practices in nested-null-from-java handling  :)
We have a java object foo and we want to work with it's deeply nested member g
foo.getA.getB.getC.getD.getE.getF.getG
How would you access g if you know that each nested member can be null and you don't want to explicitly declare reference and checking for each nested member?
Thanks!

Simon Schäfer

unread,
Feb 7, 2013, 4:00:47 AM2/7/13
to scala-l...@googlegroups.com
Just use Option and for-comprehensions:

for {
f <- Option(foo)
a <- Option(f.getA)
b <- Option(a.getB)
c <- Option(b.getC)
} yield c

On Thu 07 Feb 2013 09:50:54 AM CET, Piotr Kopeć wrote:
> Hi All,
> I want to ask you about best practices in nested-null-from-java
> handling :)
> We have a java object *foo* and we want to work with it's deeply
> nested member *g
> *
>
> *foo*.getA.getB.getC.getD.getE.getF.getG
>
> How would you access *g* if you know that each nested member can be
> null and you don't want to explicitly declare reference and checking
> for each nested member?
> Thanks!
>
> --
> You received this message because you are subscribed to the Google
> Groups "scala-language" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to scala-languag...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>

Piotr Kopeć

unread,
Feb 7, 2013, 4:08:46 AM2/7/13
to scala-l...@googlegroups.com
It looks great, thank you Simon!
As I see now, I have to rearrange many cogs in my head
Have a great day
P.

Klaus Havelund

unread,
Feb 7, 2013, 4:55:04 PM2/7/13
to scala-l...@googlegroups.com
It seems as if Scala 2.10.1 is not compatible with previous versions
of Scala. Is there some documentation of
where it differs?

Klaus

Jason Zaugg

unread,
Feb 7, 2013, 5:41:03 PM2/7/13
to scala-l...@googlegroups.com
It isn't released yet. What you basing your claim on?

We intend to release 2.10.1 as forward and backward binary compatible with 2.10.0.

Any exceptions to this will be included in the release notes (for example, in scala-reflect.jar, the contents of the package `scala.reflect.internal` is taken to be an implementation not a public API, and is not subject to the same compatibility constraints.)

-jason 

Klaus Havelund

unread,
Feb 7, 2013, 5:42:27 PM2/7/13
to scala-l...@googlegroups.com
Sorry, I mean 2.10.0

Klaus

Som Snytt

unread,
Feb 8, 2013, 1:41:32 AM2/8/13
to scala-l...@googlegroups.com
Since Simon gave up after "getC", and if you weren't asking for "best" practice:

Try(foo.getA.getB.getC.getD.getE.getF.getG) recover { case _: NPE => something } toOption

Maybe best practice would be a macro, optionally(foo.getA.getB.getC.getD.getE.getF.getG).


On Thu, Feb 7, 2013 at 1:00 AM, Simon Schäfer <ma...@antoras.de> wrote:
Just use Option and for-comprehensions:

for {
 f <- Option(foo)
 a <- Option(f.getA)
 b <- Option(a.getB)
 c <- Option(b.getC)
} yield c


On Thu 07 Feb 2013 09:50:54 AM CET, Piotr Kopeć wrote:
Hi All,
I want to ask you about best practices in nested-null-from-java
handling  :)
We have a java object *foo* and we want to work with it's deeply
nested member *g
*

    *foo*.getA.getB.getC.getD.getE.getF.getG

How would you access *g* if you know that each nested member can be

null and you don't want to explicitly declare reference and checking
for each nested member?
Thanks!

--
You received this message because you are subscribed to the Google
Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send

For more options, visit https://groups.google.com/groups/opt_out.


--
You received this message because you are subscribed to the Google Groups "scala-language" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-language+unsubscribe@googlegroups.com.

Ken Scambler

unread,
Feb 8, 2013, 3:03:03 AM2/8/13
to scala-l...@googlegroups.com

Try(foo.getA.getB.getC.getD.getE.getF.getG) recover { case _: NPE => something } toOption

What does this usage of Try/recover give you over try/catch?
 
Maybe best practice would be a macro, optionally(foo.getA.getB.getC.getD.getE.getF.getG).

I don't think reaching for the macro cupboard is best practice unless you reaaaally need it.
 

Oliver Ruebenacker

unread,
Feb 8, 2013, 8:50:10 AM2/8/13
to scala-l...@googlegroups.com
Hello,

I used to be told that throwing exceptions should be avoided if
normal control structures would suffice.

How about (untested):

val resultOption = (new
Option(foo.getA)).map(_.getB).map(_.getC).map(_.getD).map(_.getE).map(_.getF).map(_.getG)

Take care
Oliver
> --
> You received this message because you are subscribed to the Google Groups
> "scala-language" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to scala-languag...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



--
IT Project Lead at PanGenX (http://www.pangenx.com)
The purpose is always improvement

Simon Schäfer

unread,
Feb 8, 2013, 9:04:43 AM2/8/13
to scala-l...@googlegroups.com
Hi Oliver,

On Fri 08 Feb 2013 02:50:10 PM CET, Oliver Ruebenacker wrote:
>
> Hello,
>
> I used to be told that throwing exceptions should be avoided if
> normal control structures would suffice.
Yes, that is true. But sometimes it is the more succinct solution.
Because Try, which is added in 2.10, provides a monadic way to operate
on exceptions, I don't think it is a mistake to use it if it is
appropriate.
>
> How about (untested):
>
> val resultOption = (new
> Option(foo.getA)).map(_.getB).map(_.getC).map(_.getD).map(_.getE).map(_.getF).map(_.getG)

This does not work since Some can contain null. You always need to go
the indirection through Option.apply otherwise this is not NPE safe.

Furthermore, because map would nest an Option into another one, one need
to use flatMap instead, which results in the identical solution as the
shown for-comprehension.

Oliver Ruebenacker

unread,
Feb 8, 2013, 9:52:00 AM2/8/13
to scala-l...@googlegroups.com
Hello,

scala> val len1 = Option("Hello World!").map(_.length).map(2*_)
len1: Option[Int] = Some(24)

scala> val len1 = Option(null : String).map(_.length).map(2*_)
len1: Option[Int] = None

:)

Take care
Oliver

Alec Zorab

unread,
Feb 8, 2013, 9:56:24 AM2/8/13
to scala-l...@googlegroups.com
scala> case class A(r: AnyRef)
defined class A

scala>   Option(A(null)).map(_.r).map(_.hashCode)
java.lang.NullPointerException

Oliver Ruebenacker

unread,
Feb 8, 2013, 10:00:11 AM2/8/13
to scala-l...@googlegroups.com
Hello,

On Fri, Feb 8, 2013 at 9:56 AM, Alec Zorab <alec...@gmail.com> wrote:
> scala> case class A(r: AnyRef)
> defined class A
>
> scala> Option(A(null)).map(_.r).map(_.hashCode)
> java.lang.NullPointerException

Aw, sucks!

Shouldn't Option.map return None rather than Some(null)?

Take care
Oliver

Alec Zorab

unread,
Feb 8, 2013, 10:56:52 AM2/8/13
to scala-l...@googlegroups.com
Some(null) is an entirely legitimate instance of Some.

Legitimate in that you can construct it, not in that it's a particularly good idea to use.

John Nilsson

unread,
Feb 8, 2013, 11:54:51 AM2/8/13
to scala-l...@googlegroups.com

Shouldn't "best practice" rather be to apply the tell don't ask principle? Or at least the law of demeter? Or does FP invalidate those guidelines?

BR
John

To unsubscribe from this group and stop receiving emails from it, send an email to scala-languag...@googlegroups.com.

Lars Hupel

unread,
Feb 13, 2013, 3:40:31 AM2/13/13
to scala-l...@googlegroups.com
> Shouldn't Option.map return None rather than Some(null)?

That would violate the Functor laws. (In the sense that `x.map(identity)
== x` breaks.)

Note that the same discussion has been held for `Try.map`, but with a
different conclusion (cf <https://issues.scala-lang.org/browse/SI-6284>).

Andrew Phillips

unread,
Feb 13, 2013, 12:35:54 PM2/13/13
to scala-l...@googlegroups.com
> Legitimate in that you can construct it, not in that it's a particularly good idea to use.

...which is perhaps also reflected by the fact that Option(null) - as opposed to an explicit Some(null) - returns None:

scala> Option(null)
res0: Option[Null] = None

ap

Kevin Wright

unread,
Feb 13, 2013, 12:41:55 PM2/13/13
to scala-l...@googlegroups.com
Imagine you have a Map, myMap, containing the key/value pair `42 -> null`

Now call `myMap(42)`

What is the return value?  How is this district from the returned value if it didn't contain the key 42?



In an ideal world, maps couldn't contain null values.  But it's not something that can be avoided in the face of Java interop.

Kevin Wright
mail: kevin....@scalatechnology.com
gtalk / msn : kev.lee...@gmail.com
vibe / skype: kev.lee.wright
steam: kev_lee_wright

"My point today is that, if we wish to count lines of code, we should not regard them as "lines produced" but as "lines spent": the current conventional wisdom is so foolish as to book that count on the wrong side of the ledger" ~ Dijkstra

Lex Spoon

unread,
Feb 13, 2013, 1:07:57 PM2/13/13
to scala-l...@googlegroups.com
On Fri, Feb 8, 2013 at 10:56 AM, Alec Zorab <alec...@gmail.com> wrote:
> Some(null) is an entirely legitimate instance of Some.
>
> Legitimate in that you can construct it, not in that it's a particularly
> good idea to use.

It looks silly by itself, but polymorphic code will quickly run you
into cases where you expect basic identities to hold true. As a simple
example, imagine you write a function that returns Option[T] to
indicate success. It returns a Some[T] when things are good, and it
returns None to indicate that something went wrong.

Now you try to use this code in tandem with Java code that--in Java
style--is using null to represent an optional value of its own. In
such a case, you really do want to distinguish between None and
Some(null). The former means the computation failed, and the latter
means that it succeeded but return null.

By similar reasoning, you need to represent things like Some(None) in
order to combine Scala code together. You can't just map Some(None) to
None or you'll break assumptions that the programmer made. For
example, programmers normally assume that Some(x).get will give you
back whatever x was.

If all of this sounds pedantic, note that the source code might not
include any explicit mention of types like Option[Option[Foo]] . All
it takes is a function using type Option[A], and a call to that
function using A=B, and a call to *that* function using B=Option[C].

-Lex

Paul Phillips

unread,
Feb 13, 2013, 1:16:55 PM2/13/13
to scala-l...@googlegroups.com

On Wed, Feb 13, 2013 at 10:07 AM, Lex Spoon <l...@lexspoon.org> wrote:
Now you try to use this code in tandem with Java code that--in Java
style--is using null to represent an optional value of its own. In
such a case, you really do want to distinguish between None and
Some(null). The former means the computation failed, and the latter
means that it succeeded but return null.

It is easy to witness this in practice, as we did years ago when the idea of disallowing Some(null) appeared plausible, in the collections:

  def find(pred: A => Boolean): Option[A]

The problem with Option is that it carries only a single extra bit. As soon as that bit is used to mean something specific in some context, every other possible use is precluded in that same context. At least two meanings are already pervasive: "this is not null" and "this is not absent". These alone are irreconcilable, because they might be used in almost any context. And there are more meanings too.

I have long hoped to resolve this with a new Option, which offers the same manipulations but is abstract in terms of what that one bit means. Then we have one bit for each concrete usage (e.g. SomeNotNull, SomeNotAbsent, not that I suggest those horrible names) and they can be composed without stomping on each Option's bit of interest.

Rex Kerr

unread,
Feb 13, 2013, 1:36:09 PM2/13/13
to scala-l...@googlegroups.com

Why this way rather than None, SomeNull?

  --Rex

Paul Phillips

unread,
Feb 13, 2013, 1:43:12 PM2/13/13
to scala-l...@googlegroups.com

On Wed, Feb 13, 2013 at 10:36 AM, Rex Kerr <ich...@gmail.com> wrote:
Why this way rather than None, SomeNull?

You mean there None / SomeNull / Some(T) are peers? That is plausible in terms of reconciling these two uses, and would be a solid incremental improvement, but it does not address the general issue of "too few bits, too many meanings."

It would also (if we're talking about the "real" Option) potentially break all the existing pattern matches which look like

  case Some(x) =>
  case None =>

Now it's true that those matches will already throw a MatchError if you give them null - but now they'd throw it in two different circumstances, because "null" would still be a threat, only now joined by SomeNull.

Rex Kerr

unread,
Feb 13, 2013, 1:51:43 PM2/13/13
to scala-l...@googlegroups.com

Ah, so Some would have child classes SomeNotAbsent, SomeNotNull.  I somehow missed that before.

  --Rex

Oliver Ruebenacker

unread,
Feb 13, 2013, 6:26:14 PM2/13/13
to scala-l...@googlegroups.com
Hello,
How about adding a method denull to Option, to turn Some(null) into None:

def denull = if(this == Some(null)) None else this

I think it would be very useful. In the use case of this thread, we
could then write:

val resultOption =
Option(foo).denull.map(_.getA).denull.map(_.getB).denull.map(_.getC).denull.map(_.getD).denull.map(_.getE).denull.map(_.getF).denull.map(_.getG).denull

What do you think?

Take care
Oliver
Reply all
Reply to author
Forward
0 new messages