117
votes

Suppose I have a method session.get(str: String): String but you don't know whether it will return you a string or a null, because it comes from Java.

Is there an easier way to treat this in Scala instead of session.get("foo") == null ? Maybe some magic apply like ToOption(session.get("foo")) and then I can treat it in Scala way like

ToOption(session.get("foo")) match {
    case Some(_) =>;
    case None =>;
}
4

4 Answers

201
votes

The Option companion object's apply method serves as a conversion function from nullable references:

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

scala> Option(3)   
res5: Option[Int] = Some(3)
20
votes

The Option object has an applymethod that does exactly that:

var myOptionalString = Option(session.get("foo"));
5
votes

Notice that when working with Java objects it won't work as expected:

val nullValueInteger : java.lang.Integer = null
val option: Option[Int] = Option(nullValueInteger)
println(option)  // Doesn't work - zero value on conversion

val nullStringValue : String = null
val optionString: Option[String] = Option(nullStringValue)
println(optionString) // Works - None value
-2
votes

This is a very old topic but a nice one!

It's true that converting any Non-exception result of Try to Option will result in a Some...

scala> Try(null).toOption
res10: Option[Null] = Some(null)

...because Try is not about nullability checking but just a way to functionally handle exceptions.

Using Try to catch an exception and converting that to an Option for convenience will only show None in case an exception happens.

scala> Try(1/0).toOption
res11: Option[Int] = None

You want to preserve the values that come out of Try. That may be null.

But it is also true that the standard lib is quite confusing sometimes...

scala> Try(null).toOption
res12: Option[Null] = Some(null)

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

This behaviour is a bit inconsistent but it kind of reflects the intented usage of both Try and Option.

You use try to get whatever comes out of an expression that may throw exceptions, and you don't care about the exception itself.

The value that may come out may very well be a null. If toOption gave None, you could not differenciate between an exception and a null, and that is not pretty!

Standalone, you use Option to encapsulate the existence or not of something. So in that case Some(null) is None, and that makes sense, because null in that case represents the absence of something. There's no ambiguity here.

It's important to remark that in any case referencial transparency is not broken since .toOption is not the same as Option()

If you really need to enforce BOTH exception safety AND null safety, and your code really really doesn't need to differenciate between null and an exception, you just need to combine both paradigms! Because well, that's what you want, right?

You can do it in one way...

scala> Try(Option(null)).getOrElse(None)
res23: Option[Null] = None

scala> Try(Option(3/0)).getOrElse(None)
res24: Option[Int] = None

scala> Try(Option(3)).getOrElse(None)
res25: Option[Int] = Some(3)

... or another ...

scala> Try(Option(null)).toOption.flatten
res26: Option[Null] = None

scala> Try(Option(3/0)).toOption.flatten
res27: Option[Int] = None

scala> Try(Option(3)).toOption.flatten
res28: Option[Int] = Some(3)

... or the ridiculously ugliest of them anothers ...

scala> Option(Try(null).getOrElse(null))
res29: Option[Null] = None

scala> Option(Try(3/0).getOrElse(null))
res30: Option[Any] = None

scala> Option(Try(3).getOrElse(null))
res31: Option[Any] = Some(3)